diff --git a/build.ps1 b/build.ps1 index 2dbe9b0835..124320fdb8 100644 --- a/build.ps1 +++ b/build.ps1 @@ -23,7 +23,7 @@ if ($RunTests) foreach ($csproj_file in $unit_csproj_file, $integration_csproj_file, $async_integration_csproj_file, $sequential_integration_csproj_file) { Write-Host "[INFO] running Unit / Integration tests from '$csproj_file' (all frameworks)" -ForegroundColor "Magenta" - dotnet test $csproj_file --no-restore --no-build --logger "console;verbosity=detailed" + dotnet test $csproj_file --environment 'RABBITMQ_LONG_RUNNING_TESTS=true' --no-restore --no-build --logger "console;verbosity=detailed" if ($LASTEXITCODE -ne 0) { Write-Host "[ERROR] tests errored, exiting" -Foreground "Red" diff --git a/projects/Benchmarks/Benchmarks.csproj b/projects/Benchmarks/Benchmarks.csproj index 19bfe18434..56a308e404 100644 --- a/projects/Benchmarks/Benchmarks.csproj +++ b/projects/Benchmarks/Benchmarks.csproj @@ -15,7 +15,7 @@ - + diff --git a/projects/Benchmarks/ConsumerDispatching/AsyncBasicConsumerFake.cs b/projects/Benchmarks/ConsumerDispatching/AsyncBasicConsumerFake.cs index d9cf669f42..80c5fb9ce0 100644 --- a/projects/Benchmarks/ConsumerDispatching/AsyncBasicConsumerFake.cs +++ b/projects/Benchmarks/ConsumerDispatching/AsyncBasicConsumerFake.cs @@ -29,14 +29,15 @@ public AsyncBasicConsumerFake(ManualResetEventSlim autoResetEvent) return Task.CompletedTask; } - void IBasicConsumer.HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - in ReadOnlyBasicProperties properties, ReadOnlyMemory body) + Task IBasicConsumer.HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, + ReadOnlyBasicProperties properties, ReadOnlyMemory body) { if (Interlocked.Increment(ref _current) == Count) { _current = 0; _autoResetEvent.Set(); } + return Task.CompletedTask; } public Task HandleBasicCancel(string consumerTag) => Task.CompletedTask; diff --git a/projects/Benchmarks/Networking/Networking_BasicDeliver_Commons.cs b/projects/Benchmarks/Networking/Networking_BasicDeliver_Commons.cs index 16a9f537cd..b4bb341ece 100644 --- a/projects/Benchmarks/Networking/Networking_BasicDeliver_Commons.cs +++ b/projects/Benchmarks/Networking/Networking_BasicDeliver_Commons.cs @@ -12,10 +12,10 @@ public class Networking_BasicDeliver_Commons public static async Task Publish_Hello_World(IConnection connection, uint messageCount, byte[] body) { var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - using (var channel = connection.CreateChannel()) + using (IChannel channel = await connection.CreateChannelAsync()) { - var queue = channel.QueueDeclare(); - var consumed = 0; + QueueDeclareOk queue = await channel.QueueDeclareAsync(); + int consumed = 0; var consumer = new EventingBasicConsumer(channel); consumer.Received += (s, args) => { @@ -24,14 +24,15 @@ public static async Task Publish_Hello_World(IConnection connection, uint messag tcs.SetResult(true); } }; - channel.BasicConsume(queue.QueueName, true, consumer); + await channel.BasicConsumeAsync(queue.QueueName, true, consumer); for (int i = 0; i < messageCount; i++) { - channel.BasicPublish("", queue.QueueName, body); + await channel.BasicPublishAsync("", queue.QueueName, body); } await tcs.Task; + await channel.CloseAsync(); } } } diff --git a/projects/Benchmarks/Networking/Networking_BasicDeliver_ConnectionChurn.cs b/projects/Benchmarks/Networking/Networking_BasicDeliver_ConnectionChurn.cs index b3003a3468..a2542b9037 100644 --- a/projects/Benchmarks/Networking/Networking_BasicDeliver_ConnectionChurn.cs +++ b/projects/Benchmarks/Networking/Networking_BasicDeliver_ConnectionChurn.cs @@ -30,7 +30,7 @@ public void GlobalCleanup() public async Task Publish_Hello_World() { var cf = new ConnectionFactory { ConsumerDispatchConcurrency = 2 }; - using (var connection = cf.CreateConnection()) + using (IConnection connection = await cf.CreateConnectionAsync()) { await Publish_Hello_World(connection); } diff --git a/projects/Benchmarks/Networking/Networking_BasicDeliver_LongLivedConnection.cs b/projects/Benchmarks/Networking/Networking_BasicDeliver_LongLivedConnection.cs index 6045382ee5..d60adbdc3d 100644 --- a/projects/Benchmarks/Networking/Networking_BasicDeliver_LongLivedConnection.cs +++ b/projects/Benchmarks/Networking/Networking_BasicDeliver_LongLivedConnection.cs @@ -21,7 +21,8 @@ public void GlobalSetup() _container = RabbitMQBroker.Start(); var cf = new ConnectionFactory { ConsumerDispatchConcurrency = 2 }; - _connection = cf.CreateConnection(); + // TODO / NOTE: https://github.com/dotnet/BenchmarkDotNet/issues/1738 + _connection = cf.CreateConnectionAsync().EnsureCompleted(); } [GlobalCleanup] diff --git a/projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs b/projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs index 9a8abb7ee5..ce93659a93 100644 --- a/projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs +++ b/projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs @@ -34,23 +34,22 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; -using System.Text.Json.Serialization; using System.Threading.Tasks; namespace RabbitMQ.Client.OAuth2 { public interface IOAuth2Client { - public IToken RequestToken(); - public IToken RefreshToken(IToken token); + IToken RequestToken(); + IToken RefreshToken(IToken token); } public interface IToken { - public string AccessToken { get; } - public string RefreshToken { get; } - public TimeSpan ExpiresIn { get; } - public bool hasExpired { get; } + string AccessToken { get; } + string RefreshToken { get; } + TimeSpan ExpiresIn { get; } + bool hasExpired { get; } } public class Token : IToken diff --git a/projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj b/projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj index 77f6d28b21..ce71a0075b 100644 --- a/projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj +++ b/projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj @@ -25,10 +25,8 @@ minimal true ../../packages - true - latest - 7.0 README.md + 7.3 @@ -58,7 +56,7 @@ - + diff --git a/projects/RabbitMQ.Client/PublicAPI.Unshipped.txt b/projects/RabbitMQ.Client/PublicAPI.Unshipped.txt index e7b41970a3..3cda2e436f 100644 --- a/projects/RabbitMQ.Client/PublicAPI.Unshipped.txt +++ b/projects/RabbitMQ.Client/PublicAPI.Unshipped.txt @@ -66,7 +66,6 @@ override RabbitMQ.Client.Events.AsyncEventingBasicConsumer.HandleBasicDeliver(st override RabbitMQ.Client.Events.AsyncEventingBasicConsumer.HandleChannelShutdown(object channel, RabbitMQ.Client.ShutdownEventArgs reason) -> System.Threading.Tasks.Task override RabbitMQ.Client.Events.EventingBasicConsumer.HandleBasicCancelOk(string consumerTag) -> void override RabbitMQ.Client.Events.EventingBasicConsumer.HandleBasicConsumeOk(string consumerTag) -> void -override RabbitMQ.Client.Events.EventingBasicConsumer.HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, in RabbitMQ.Client.ReadOnlyBasicProperties properties, System.ReadOnlyMemory body) -> void override RabbitMQ.Client.Events.EventingBasicConsumer.HandleChannelShutdown(object channel, RabbitMQ.Client.ShutdownEventArgs reason) -> void override RabbitMQ.Client.Exceptions.MalformedFrameException.ReplyCode.get -> ushort override RabbitMQ.Client.Exceptions.SyntaxErrorException.ReplyCode.get -> ushort @@ -212,20 +211,6 @@ RabbitMQ.Client.ConnectionFactory.ConsumerDispatchConcurrency.get -> int RabbitMQ.Client.ConnectionFactory.ConsumerDispatchConcurrency.set -> void RabbitMQ.Client.ConnectionFactory.ContinuationTimeout.get -> System.TimeSpan RabbitMQ.Client.ConnectionFactory.ContinuationTimeout.set -> void -RabbitMQ.Client.ConnectionFactory.CreateConnection() -> RabbitMQ.Client.IConnection -RabbitMQ.Client.ConnectionFactory.CreateConnection(RabbitMQ.Client.IEndpointResolver endpointResolver, string clientProvidedName) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.ConnectionFactory.CreateConnection(string clientProvidedName) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.ConnectionFactory.CreateConnection(System.Collections.Generic.IEnumerable endpoints) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.ConnectionFactory.CreateConnection(System.Collections.Generic.IEnumerable endpoints, string clientProvidedName) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.ConnectionFactory.CreateConnection(System.Collections.Generic.IEnumerable hostnames) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.ConnectionFactory.CreateConnection(System.Collections.Generic.IEnumerable hostnames, string clientProvidedName) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(RabbitMQ.Client.IEndpointResolver endpointResolver, string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable endpoints, string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable endpoints, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable hostnames, string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable hostnames, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask RabbitMQ.Client.ConnectionFactory.CredentialsProvider.get -> RabbitMQ.Client.ICredentialsProvider RabbitMQ.Client.ConnectionFactory.CredentialsProvider.set -> void RabbitMQ.Client.ConnectionFactory.CredentialsRefresher.get -> RabbitMQ.Client.ICredentialsRefresher @@ -442,7 +427,6 @@ RabbitMQ.Client.IBasicConsumer.ConsumerCancelled -> System.EventHandler void RabbitMQ.Client.IBasicConsumer.HandleBasicCancelOk(string consumerTag) -> void RabbitMQ.Client.IBasicConsumer.HandleBasicConsumeOk(string consumerTag) -> void -RabbitMQ.Client.IBasicConsumer.HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, in RabbitMQ.Client.ReadOnlyBasicProperties properties, System.ReadOnlyMemory body) -> void RabbitMQ.Client.IBasicConsumer.HandleChannelShutdown(object channel, RabbitMQ.Client.ShutdownEventArgs reason) -> void RabbitMQ.Client.IBasicProperties RabbitMQ.Client.IBasicProperties.AppId.get -> string @@ -492,84 +476,28 @@ RabbitMQ.Client.IBasicProperties.Type.set -> void RabbitMQ.Client.IBasicProperties.UserId.get -> string RabbitMQ.Client.IBasicProperties.UserId.set -> void RabbitMQ.Client.IChannel -RabbitMQ.Client.IChannel.BasicAck(ulong deliveryTag, bool multiple) -> void RabbitMQ.Client.IChannel.BasicAckAsync(ulong deliveryTag, bool multiple) -> System.Threading.Tasks.ValueTask RabbitMQ.Client.IChannel.BasicAcks -> System.EventHandler -RabbitMQ.Client.IChannel.BasicCancel(string consumerTag) -> void -RabbitMQ.Client.IChannel.BasicCancelAsync(string consumerTag) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.BasicCancelNoWait(string consumerTag) -> void -RabbitMQ.Client.IChannel.BasicConsume(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, System.Collections.Generic.IDictionary arguments, RabbitMQ.Client.IBasicConsumer consumer) -> string -RabbitMQ.Client.IChannel.BasicConsumeAsync(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, System.Collections.Generic.IDictionary arguments, RabbitMQ.Client.IBasicConsumer consumer) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.BasicGet(string queue, bool autoAck) -> RabbitMQ.Client.BasicGetResult RabbitMQ.Client.IChannel.BasicGetAsync(string queue, bool autoAck) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.BasicNack(ulong deliveryTag, bool multiple, bool requeue) -> void RabbitMQ.Client.IChannel.BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue) -> System.Threading.Tasks.ValueTask RabbitMQ.Client.IChannel.BasicNacks -> System.EventHandler -RabbitMQ.Client.IChannel.BasicPublish(RabbitMQ.Client.CachedString exchange, RabbitMQ.Client.CachedString routingKey, in TProperties basicProperties, System.ReadOnlyMemory body = default(System.ReadOnlyMemory), bool mandatory = false) -> void -RabbitMQ.Client.IChannel.BasicPublish(string exchange, string routingKey, in TProperties basicProperties, System.ReadOnlyMemory body = default(System.ReadOnlyMemory), bool mandatory = false) -> void RabbitMQ.Client.IChannel.BasicPublishAsync(RabbitMQ.Client.CachedString exchange, RabbitMQ.Client.CachedString routingKey, in TProperties basicProperties, System.ReadOnlyMemory body = default(System.ReadOnlyMemory), bool mandatory = false) -> System.Threading.Tasks.ValueTask RabbitMQ.Client.IChannel.BasicPublishAsync(string exchange, string routingKey, in TProperties basicProperties, System.ReadOnlyMemory body = default(System.ReadOnlyMemory), bool mandatory = false) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.BasicQos(uint prefetchSize, ushort prefetchCount, bool global) -> void -RabbitMQ.Client.IChannel.BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.BasicReject(ulong deliveryTag, bool requeue) -> void -RabbitMQ.Client.IChannel.BasicRejectAsync(ulong deliveryTag, bool requeue) -> System.Threading.Tasks.ValueTask RabbitMQ.Client.IChannel.BasicReturn -> System.EventHandler RabbitMQ.Client.IChannel.CallbackException -> System.EventHandler RabbitMQ.Client.IChannel.ChannelNumber.get -> int RabbitMQ.Client.IChannel.ChannelShutdown -> System.EventHandler -RabbitMQ.Client.IChannel.Close(ushort replyCode, string replyText, bool abort) -> void -RabbitMQ.Client.IChannel.CloseAsync(RabbitMQ.Client.ShutdownEventArgs reason, bool abort) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.CloseAsync(ushort replyCode, string replyText, bool abort) -> System.Threading.Tasks.ValueTask RabbitMQ.Client.IChannel.CloseReason.get -> RabbitMQ.Client.ShutdownEventArgs -RabbitMQ.Client.IChannel.ConfirmSelect() -> void -RabbitMQ.Client.IChannel.ConfirmSelectAsync() -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.ConsumerCount(string queue) -> uint RabbitMQ.Client.IChannel.ContinuationTimeout.get -> System.TimeSpan RabbitMQ.Client.IChannel.ContinuationTimeout.set -> void RabbitMQ.Client.IChannel.CurrentQueue.get -> string RabbitMQ.Client.IChannel.DefaultConsumer.get -> RabbitMQ.Client.IBasicConsumer RabbitMQ.Client.IChannel.DefaultConsumer.set -> void -RabbitMQ.Client.IChannel.ExchangeBind(string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments) -> void -RabbitMQ.Client.IChannel.ExchangeBindAsync(string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.ExchangeBindNoWait(string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments) -> void -RabbitMQ.Client.IChannel.ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete, System.Collections.Generic.IDictionary arguments) -> void -RabbitMQ.Client.IChannel.ExchangeDeclareAsync(string exchange, string type, bool passive, bool durable, bool autoDelete, System.Collections.Generic.IDictionary arguments) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.ExchangeDeclareNoWait(string exchange, string type, bool durable, bool autoDelete, System.Collections.Generic.IDictionary arguments) -> void -RabbitMQ.Client.IChannel.ExchangeDeclarePassive(string exchange) -> void -RabbitMQ.Client.IChannel.ExchangeDelete(string exchange, bool ifUnused) -> void -RabbitMQ.Client.IChannel.ExchangeDeleteAsync(string exchange, bool ifUnused) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.ExchangeDeleteNoWait(string exchange, bool ifUnused) -> void -RabbitMQ.Client.IChannel.ExchangeUnbind(string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments) -> void -RabbitMQ.Client.IChannel.ExchangeUnbindAsync(string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.ExchangeUnbindNoWait(string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments) -> void RabbitMQ.Client.IChannel.FlowControl -> System.EventHandler RabbitMQ.Client.IChannel.IsClosed.get -> bool RabbitMQ.Client.IChannel.IsOpen.get -> bool -RabbitMQ.Client.IChannel.MessageCount(string queue) -> uint RabbitMQ.Client.IChannel.NextPublishSeqNo.get -> ulong -RabbitMQ.Client.IChannel.QueueBind(string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments) -> void -RabbitMQ.Client.IChannel.QueueBindAsync(string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.QueueBindNoWait(string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments) -> void -RabbitMQ.Client.IChannel.QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, System.Collections.Generic.IDictionary arguments) -> RabbitMQ.Client.QueueDeclareOk -RabbitMQ.Client.IChannel.QueueDeclareAsync(string queue, bool passive, bool durable, bool exclusive, bool autoDelete, System.Collections.Generic.IDictionary arguments) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.QueueDeclareNoWait(string queue, bool durable, bool exclusive, bool autoDelete, System.Collections.Generic.IDictionary arguments) -> void -RabbitMQ.Client.IChannel.QueueDeclarePassive(string queue) -> RabbitMQ.Client.QueueDeclareOk -RabbitMQ.Client.IChannel.QueueDelete(string queue, bool ifUnused, bool ifEmpty) -> uint -RabbitMQ.Client.IChannel.QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.QueueDeleteNoWait(string queue, bool ifUnused, bool ifEmpty) -> void -RabbitMQ.Client.IChannel.QueuePurge(string queue) -> uint -RabbitMQ.Client.IChannel.QueuePurgeAsync(string queue) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.QueueUnbind(string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments) -> void -RabbitMQ.Client.IChannel.QueueUnbindAsync(string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.TxCommit() -> void -RabbitMQ.Client.IChannel.TxCommitAsync() -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.TxRollback() -> void -RabbitMQ.Client.IChannel.TxRollbackAsync() -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.TxSelect() -> void -RabbitMQ.Client.IChannel.TxSelectAsync() -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IChannel.WaitForConfirms() -> bool RabbitMQ.Client.IChannel.WaitForConfirmsAsync(System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task -RabbitMQ.Client.IChannel.WaitForConfirmsOrDie() -> void RabbitMQ.Client.IChannel.WaitForConfirmsOrDieAsync(System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task RabbitMQ.Client.IChannelExtensions RabbitMQ.Client.IConnection @@ -577,16 +505,12 @@ RabbitMQ.Client.IConnection.CallbackException -> System.EventHandler ushort RabbitMQ.Client.IConnection.ClientProperties.get -> System.Collections.Generic.IDictionary RabbitMQ.Client.IConnection.ClientProvidedName.get -> string -RabbitMQ.Client.IConnection.Close(ushort reasonCode, string reasonText, System.TimeSpan timeout, bool abort) -> void -RabbitMQ.Client.IConnection.CloseAsync(ushort reasonCode, string reasonText, System.TimeSpan timeout, bool abort) -> System.Threading.Tasks.ValueTask RabbitMQ.Client.IConnection.CloseReason.get -> RabbitMQ.Client.ShutdownEventArgs RabbitMQ.Client.IConnection.ConnectionBlocked -> System.EventHandler RabbitMQ.Client.IConnection.ConnectionRecoveryError -> System.EventHandler RabbitMQ.Client.IConnection.ConnectionShutdown -> System.EventHandler RabbitMQ.Client.IConnection.ConnectionUnblocked -> System.EventHandler RabbitMQ.Client.IConnection.ConsumerTagChangeAfterRecovery -> System.EventHandler -RabbitMQ.Client.IConnection.CreateChannel() -> RabbitMQ.Client.IChannel -RabbitMQ.Client.IConnection.CreateChannelAsync() -> System.Threading.Tasks.ValueTask RabbitMQ.Client.IConnection.Endpoint.get -> RabbitMQ.Client.AmqpTcpEndpoint RabbitMQ.Client.IConnection.FrameMax.get -> uint RabbitMQ.Client.IConnection.Heartbeat.get -> System.TimeSpan @@ -597,7 +521,6 @@ RabbitMQ.Client.IConnection.RecoveringConsumer -> System.EventHandler System.EventHandler RabbitMQ.Client.IConnection.ServerProperties.get -> System.Collections.Generic.IDictionary RabbitMQ.Client.IConnection.ShutdownReport.get -> System.Collections.Generic.IEnumerable -RabbitMQ.Client.IConnection.UpdateSecret(string newSecret, string reason) -> void RabbitMQ.Client.IConnectionExtensions RabbitMQ.Client.IConnectionFactory RabbitMQ.Client.IConnectionFactory.AuthMechanismFactory(System.Collections.Generic.IEnumerable mechanismNames) -> RabbitMQ.Client.IAuthMechanismFactory @@ -609,18 +532,6 @@ RabbitMQ.Client.IConnectionFactory.ConsumerDispatchConcurrency.get -> int RabbitMQ.Client.IConnectionFactory.ConsumerDispatchConcurrency.set -> void RabbitMQ.Client.IConnectionFactory.ContinuationTimeout.get -> System.TimeSpan RabbitMQ.Client.IConnectionFactory.ContinuationTimeout.set -> void -RabbitMQ.Client.IConnectionFactory.CreateConnection() -> RabbitMQ.Client.IConnection -RabbitMQ.Client.IConnectionFactory.CreateConnection(string clientProvidedName) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.IConnectionFactory.CreateConnection(System.Collections.Generic.IEnumerable endpoints) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.IConnectionFactory.CreateConnection(System.Collections.Generic.IEnumerable endpoints, string clientProvidedName) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.IConnectionFactory.CreateConnection(System.Collections.Generic.IEnumerable hostnames) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.IConnectionFactory.CreateConnection(System.Collections.Generic.IEnumerable hostnames, string clientProvidedName) -> RabbitMQ.Client.IConnection -RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable endpoints, string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable endpoints, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable hostnames, string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable hostnames, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask RabbitMQ.Client.IConnectionFactory.CredentialsProvider.get -> RabbitMQ.Client.ICredentialsProvider RabbitMQ.Client.IConnectionFactory.CredentialsProvider.set -> void RabbitMQ.Client.IConnectionFactory.CredentialsRefresher.get -> RabbitMQ.Client.ICredentialsRefresher @@ -650,8 +561,7 @@ RabbitMQ.Client.ICredentialsProvider.Refresh() -> void RabbitMQ.Client.ICredentialsProvider.UserName.get -> string RabbitMQ.Client.ICredentialsProvider.ValidUntil.get -> System.TimeSpan? RabbitMQ.Client.ICredentialsRefresher -RabbitMQ.Client.ICredentialsRefresher.NotifyCredentialRefreshed -RabbitMQ.Client.ICredentialsRefresher.Register(RabbitMQ.Client.ICredentialsProvider provider, RabbitMQ.Client.ICredentialsRefresher.NotifyCredentialRefreshed callback) -> RabbitMQ.Client.ICredentialsProvider +RabbitMQ.Client.ICredentialsRefresher.NotifyCredentialRefreshedAsync RabbitMQ.Client.ICredentialsRefresher.Unregister(RabbitMQ.Client.ICredentialsProvider provider) -> bool RabbitMQ.Client.IEndpointResolver RabbitMQ.Client.IEndpointResolver.All() -> System.Collections.Generic.IEnumerable @@ -829,7 +739,6 @@ RabbitMQ.Client.TcpClientAdapter RabbitMQ.Client.TcpClientAdapter.Dispose() -> void RabbitMQ.Client.TcpClientAdapter.TcpClientAdapter(System.Net.Sockets.Socket socket) -> void RabbitMQ.Client.TimerBasedCredentialRefresher -RabbitMQ.Client.TimerBasedCredentialRefresher.Register(RabbitMQ.Client.ICredentialsProvider provider, RabbitMQ.Client.ICredentialsRefresher.NotifyCredentialRefreshed callback) -> RabbitMQ.Client.ICredentialsProvider RabbitMQ.Client.TimerBasedCredentialRefresher.TimerBasedCredentialRefresher() -> void RabbitMQ.Client.TimerBasedCredentialRefresher.Unregister(RabbitMQ.Client.ICredentialsProvider provider) -> bool RabbitMQ.Client.TimerBasedCredentialRefresherEventSource @@ -843,20 +752,12 @@ RabbitMQ.Client.TimerBasedCredentialRefresherEventSource.Unregistered(string nam RabbitMQ.Client.TopologyRecoveryExceptionHandler RabbitMQ.Client.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionCondition.get -> System.Func RabbitMQ.Client.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionCondition.set -> void -RabbitMQ.Client.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionHandler.get -> System.Action -RabbitMQ.Client.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionHandler.set -> void RabbitMQ.Client.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionCondition.get -> System.Func RabbitMQ.Client.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionCondition.set -> void -RabbitMQ.Client.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionHandler.get -> System.Action -RabbitMQ.Client.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionHandler.set -> void RabbitMQ.Client.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionCondition.get -> System.Func RabbitMQ.Client.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionCondition.set -> void -RabbitMQ.Client.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionHandler.get -> System.Action -RabbitMQ.Client.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionHandler.set -> void RabbitMQ.Client.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionCondition.get -> System.Func RabbitMQ.Client.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionCondition.set -> void -RabbitMQ.Client.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionHandler.get -> System.Action -RabbitMQ.Client.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionHandler.set -> void RabbitMQ.Client.TopologyRecoveryExceptionHandler.TopologyRecoveryExceptionHandler() -> void RabbitMQ.Client.TopologyRecoveryFilter RabbitMQ.Client.TopologyRecoveryFilter.BindingFilter.get -> System.Func @@ -944,63 +845,9 @@ static RabbitMQ.Client.EndpointResolverExtensions.SelectOneAsync(this RabbitM static RabbitMQ.Client.Events.CallbackExceptionEventArgs.Build(System.Exception e, string context) -> RabbitMQ.Client.Events.CallbackExceptionEventArgs static RabbitMQ.Client.Events.CallbackExceptionEventArgs.Build(System.Exception e, string context, object consumer) -> RabbitMQ.Client.Events.CallbackExceptionEventArgs static RabbitMQ.Client.ExchangeType.All() -> System.Collections.Generic.ICollection -static RabbitMQ.Client.IChannelExtensions.Abort(this RabbitMQ.Client.IChannel channel) -> void -static RabbitMQ.Client.IChannelExtensions.Abort(this RabbitMQ.Client.IChannel channel, ushort replyCode, string replyText) -> void -static RabbitMQ.Client.IChannelExtensions.AbortAsync(this RabbitMQ.Client.IChannel channel) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.BasicConsume(this RabbitMQ.Client.IChannel channel, RabbitMQ.Client.IBasicConsumer consumer, string queue, bool autoAck = false, string consumerTag = "", bool noLocal = false, bool exclusive = false, System.Collections.Generic.IDictionary arguments = null) -> string -static RabbitMQ.Client.IChannelExtensions.BasicConsume(this RabbitMQ.Client.IChannel channel, string queue, bool autoAck, RabbitMQ.Client.IBasicConsumer consumer) -> string -static RabbitMQ.Client.IChannelExtensions.BasicConsume(this RabbitMQ.Client.IChannel channel, string queue, bool autoAck, string consumerTag, RabbitMQ.Client.IBasicConsumer consumer) -> string -static RabbitMQ.Client.IChannelExtensions.BasicConsume(this RabbitMQ.Client.IChannel channel, string queue, bool autoAck, string consumerTag, System.Collections.Generic.IDictionary arguments, RabbitMQ.Client.IBasicConsumer consumer) -> string -static RabbitMQ.Client.IChannelExtensions.BasicConsumeAsync(this RabbitMQ.Client.IChannel channel, RabbitMQ.Client.IBasicConsumer consumer, string queue, bool autoAck = false, string consumerTag = "", bool noLocal = false, bool exclusive = false, System.Collections.Generic.IDictionary arguments = null) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.BasicConsumeAsync(this RabbitMQ.Client.IChannel channel, string queue, bool autoAck, RabbitMQ.Client.IBasicConsumer consumer) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.BasicConsumeAsync(this RabbitMQ.Client.IChannel channel, string queue, bool autoAck, string consumerTag, RabbitMQ.Client.IBasicConsumer consumer) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.BasicConsumeAsync(this RabbitMQ.Client.IChannel channel, string queue, bool autoAck, string consumerTag, System.Collections.Generic.IDictionary arguments, RabbitMQ.Client.IBasicConsumer consumer) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.BasicPublish(this RabbitMQ.Client.IChannel channel, RabbitMQ.Client.CachedString exchange, RabbitMQ.Client.CachedString routingKey, System.ReadOnlyMemory body = default(System.ReadOnlyMemory), bool mandatory = false) -> void -static RabbitMQ.Client.IChannelExtensions.BasicPublish(this RabbitMQ.Client.IChannel channel, string exchange, string routingKey, System.ReadOnlyMemory body = default(System.ReadOnlyMemory), bool mandatory = false) -> void -static RabbitMQ.Client.IChannelExtensions.BasicPublish(this RabbitMQ.Client.IChannel channel, RabbitMQ.Client.PublicationAddress addr, in T basicProperties, System.ReadOnlyMemory body) -> void static RabbitMQ.Client.IChannelExtensions.BasicPublishAsync(this RabbitMQ.Client.IChannel channel, RabbitMQ.Client.CachedString exchange, RabbitMQ.Client.CachedString routingKey, System.ReadOnlyMemory body = default(System.ReadOnlyMemory), bool mandatory = false) -> System.Threading.Tasks.ValueTask static RabbitMQ.Client.IChannelExtensions.BasicPublishAsync(this RabbitMQ.Client.IChannel channel, string exchange, string routingKey, System.ReadOnlyMemory body = default(System.ReadOnlyMemory), bool mandatory = false) -> System.Threading.Tasks.ValueTask static RabbitMQ.Client.IChannelExtensions.BasicPublishAsync(this RabbitMQ.Client.IChannel channel, RabbitMQ.Client.PublicationAddress addr, in T basicProperties, System.ReadOnlyMemory body) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.Close(this RabbitMQ.Client.IChannel channel) -> void -static RabbitMQ.Client.IChannelExtensions.Close(this RabbitMQ.Client.IChannel channel, ushort replyCode, string replyText) -> void -static RabbitMQ.Client.IChannelExtensions.CloseAsync(this RabbitMQ.Client.IChannel channel) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.CloseAsync(this RabbitMQ.Client.IChannel channel, ushort replyCode, string replyText) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.ExchangeBind(this RabbitMQ.Client.IChannel channel, string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments = null) -> void -static RabbitMQ.Client.IChannelExtensions.ExchangeBindAsync(this RabbitMQ.Client.IChannel channel, string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments = null) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.ExchangeBindNoWait(this RabbitMQ.Client.IChannel channel, string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments = null) -> void -static RabbitMQ.Client.IChannelExtensions.ExchangeDeclare(this RabbitMQ.Client.IChannel channel, string exchange, string type, bool durable = false, bool autoDelete = false, System.Collections.Generic.IDictionary arguments = null) -> void -static RabbitMQ.Client.IChannelExtensions.ExchangeDeclareAsync(this RabbitMQ.Client.IChannel channel, string exchange, string type, bool durable = false, bool autoDelete = false, System.Collections.Generic.IDictionary arguments = null) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.ExchangeDeclareNoWait(this RabbitMQ.Client.IChannel channel, string exchange, string type, bool durable = false, bool autoDelete = false, System.Collections.Generic.IDictionary arguments = null) -> void -static RabbitMQ.Client.IChannelExtensions.ExchangeDelete(this RabbitMQ.Client.IChannel channel, string exchange, bool ifUnused = false) -> void -static RabbitMQ.Client.IChannelExtensions.ExchangeDeleteAsync(this RabbitMQ.Client.IChannel channel, string exchange, bool ifUnused = false) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.ExchangeDeleteNoWait(this RabbitMQ.Client.IChannel channel, string exchange, bool ifUnused = false) -> void -static RabbitMQ.Client.IChannelExtensions.ExchangeUnbind(this RabbitMQ.Client.IChannel channel, string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments = null) -> void -static RabbitMQ.Client.IChannelExtensions.ExchangeUnbindAsync(this RabbitMQ.Client.IChannel channel, string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments = null) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.QueueBind(this RabbitMQ.Client.IChannel channel, string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments = null) -> void -static RabbitMQ.Client.IChannelExtensions.QueueBindAsync(this RabbitMQ.Client.IChannel channel, string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments = null) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.QueueDeclare(this RabbitMQ.Client.IChannel channel, string queue = "", bool durable = false, bool exclusive = true, bool autoDelete = true, System.Collections.Generic.IDictionary arguments = null) -> RabbitMQ.Client.QueueDeclareOk -static RabbitMQ.Client.IChannelExtensions.QueueDeclareAsync(this RabbitMQ.Client.IChannel channel, string queue = "", bool durable = false, bool exclusive = true, bool autoDelete = true, System.Collections.Generic.IDictionary arguments = null) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.QueueDelete(this RabbitMQ.Client.IChannel channel, string queue, bool ifUnused = false, bool ifEmpty = false) -> uint -static RabbitMQ.Client.IChannelExtensions.QueueDeleteAsync(this RabbitMQ.Client.IChannel channel, string queue, bool ifUnused = false, bool ifEmpty = false) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IChannelExtensions.QueueDeleteNoWait(this RabbitMQ.Client.IChannel channel, string queue, bool ifUnused = false, bool ifEmpty = false) -> void -static RabbitMQ.Client.IChannelExtensions.QueueUnbind(this RabbitMQ.Client.IChannel channel, string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments = null) -> void -static RabbitMQ.Client.IChannelExtensions.QueueUnbindAsync(this RabbitMQ.Client.IChannel channel, string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments = null) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IConnectionExtensions.Abort(this RabbitMQ.Client.IConnection connection) -> void -static RabbitMQ.Client.IConnectionExtensions.Abort(this RabbitMQ.Client.IConnection connection, System.TimeSpan timeout) -> void -static RabbitMQ.Client.IConnectionExtensions.Abort(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText) -> void -static RabbitMQ.Client.IConnectionExtensions.Abort(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText, System.TimeSpan timeout) -> void -static RabbitMQ.Client.IConnectionExtensions.AbortAsync(this RabbitMQ.Client.IConnection connection) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IConnectionExtensions.AbortAsync(this RabbitMQ.Client.IConnection connection, System.TimeSpan timeout) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IConnectionExtensions.AbortAsync(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IConnectionExtensions.AbortAsync(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText, System.TimeSpan timeout) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IConnectionExtensions.Close(this RabbitMQ.Client.IConnection connection) -> void -static RabbitMQ.Client.IConnectionExtensions.Close(this RabbitMQ.Client.IConnection connection, System.TimeSpan timeout) -> void -static RabbitMQ.Client.IConnectionExtensions.Close(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText) -> void -static RabbitMQ.Client.IConnectionExtensions.Close(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText, System.TimeSpan timeout) -> void -static RabbitMQ.Client.IConnectionExtensions.CloseAsync(this RabbitMQ.Client.IConnection connection) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IConnectionExtensions.CloseAsync(this RabbitMQ.Client.IConnection connection, System.TimeSpan timeout) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IConnectionExtensions.CloseAsync(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText) -> System.Threading.Tasks.ValueTask -static RabbitMQ.Client.IConnectionExtensions.CloseAsync(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText, System.TimeSpan timeout) -> System.Threading.Tasks.ValueTask static RabbitMQ.Client.PublicationAddress.Parse(string uriLikeString) -> RabbitMQ.Client.PublicationAddress static RabbitMQ.Client.PublicationAddress.TryParse(string uriLikeString, out RabbitMQ.Client.PublicationAddress result) -> bool static RabbitMQ.Client.QueueDeclareOk.implicit operator string(RabbitMQ.Client.QueueDeclareOk declareOk) -> string @@ -1023,7 +870,6 @@ virtual RabbitMQ.Client.AsyncDefaultBasicConsumer.OnCancel(params string[] consu virtual RabbitMQ.Client.DefaultBasicConsumer.HandleBasicCancel(string consumerTag) -> void virtual RabbitMQ.Client.DefaultBasicConsumer.HandleBasicCancelOk(string consumerTag) -> void virtual RabbitMQ.Client.DefaultBasicConsumer.HandleBasicConsumeOk(string consumerTag) -> void -virtual RabbitMQ.Client.DefaultBasicConsumer.HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, in RabbitMQ.Client.ReadOnlyBasicProperties properties, System.ReadOnlyMemory body) -> void virtual RabbitMQ.Client.DefaultBasicConsumer.HandleChannelShutdown(object channel, RabbitMQ.Client.ShutdownEventArgs reason) -> void virtual RabbitMQ.Client.DefaultBasicConsumer.OnCancel(params string[] consumerTags) -> void virtual RabbitMQ.Client.Exceptions.ProtocolException.ShutdownReason.get -> RabbitMQ.Client.ShutdownEventArgs @@ -1035,3 +881,74 @@ virtual RabbitMQ.Client.TcpClientAdapter.Dispose(bool disposing) -> void virtual RabbitMQ.Client.TcpClientAdapter.GetStream() -> System.Net.Sockets.NetworkStream virtual RabbitMQ.Client.TcpClientAdapter.ReceiveTimeout.get -> System.TimeSpan virtual RabbitMQ.Client.TcpClientAdapter.ReceiveTimeout.set -> void +~override RabbitMQ.Client.Events.EventingBasicConsumer.HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, RabbitMQ.Client.ReadOnlyBasicProperties properties, System.ReadOnlyMemory body) -> System.Threading.Tasks.Task +~RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(RabbitMQ.Client.IEndpointResolver endpointResolver, string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable endpoints, string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable endpoints, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable hostnames, string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable hostnames, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.ConnectionFactory.CreateConnectionAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IBasicConsumer.HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, RabbitMQ.Client.ReadOnlyBasicProperties properties, System.ReadOnlyMemory body) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.BasicCancelAsync(string consumerTag, bool noWait = false) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.BasicConsumeAsync(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, System.Collections.Generic.IDictionary arguments, RabbitMQ.Client.IBasicConsumer consumer) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.BasicRejectAsync(ulong deliveryTag, bool requeue) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.CloseAsync(RabbitMQ.Client.ShutdownEventArgs reason, bool abort) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.CloseAsync(ushort replyCode, string replyText, bool abort) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.ConfirmSelectAsync() -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.ConsumerCountAsync(string queue) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.ExchangeBindAsync(string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments = null, bool noWait = false) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.ExchangeDeclareAsync(string exchange, string type, bool durable, bool autoDelete, System.Collections.Generic.IDictionary arguments = null, bool passive = false, bool noWait = false) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.ExchangeDeclarePassiveAsync(string exchange) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.ExchangeDeleteAsync(string exchange, bool ifUnused = false, bool noWait = false) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.ExchangeUnbindAsync(string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments = null, bool noWait = false) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.MessageCountAsync(string queue) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.QueueBindAsync(string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments = null, bool noWait = false) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.QueueDeclareAsync(string queue, bool durable, bool exclusive, bool autoDelete, System.Collections.Generic.IDictionary arguments = null, bool passive = false, bool noWait = false) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.QueueDeclarePassiveAsync(string queue) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty, bool noWait = false) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.QueuePurgeAsync(string queue) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.QueueUnbindAsync(string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.TxCommitAsync() -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.TxRollbackAsync() -> System.Threading.Tasks.Task +~RabbitMQ.Client.IChannel.TxSelectAsync() -> System.Threading.Tasks.Task +~RabbitMQ.Client.IConnection.CloseAsync(ushort reasonCode, string reasonText, System.TimeSpan timeout, bool abort) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IConnection.CreateChannelAsync() -> System.Threading.Tasks.Task +~RabbitMQ.Client.IConnection.UpdateSecretAsync(string newSecret, string reason) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable endpoints, string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable endpoints, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable hostnames, string clientProvidedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(System.Collections.Generic.IEnumerable hostnames, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.IConnectionFactory.CreateConnectionAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +~RabbitMQ.Client.ICredentialsRefresher.Register(RabbitMQ.Client.ICredentialsProvider provider, RabbitMQ.Client.ICredentialsRefresher.NotifyCredentialRefreshedAsync callback) -> RabbitMQ.Client.ICredentialsProvider +~RabbitMQ.Client.TimerBasedCredentialRefresher.Register(RabbitMQ.Client.ICredentialsProvider provider, RabbitMQ.Client.ICredentialsRefresher.NotifyCredentialRefreshedAsync callback) -> RabbitMQ.Client.ICredentialsProvider +~RabbitMQ.Client.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionHandlerAsync.get -> System.Func +~RabbitMQ.Client.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionHandlerAsync.set -> void +~RabbitMQ.Client.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionHandlerAsync.get -> System.Func +~RabbitMQ.Client.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionHandlerAsync.set -> void +~RabbitMQ.Client.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionHandlerAsync.get -> System.Func +~RabbitMQ.Client.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionHandlerAsync.set -> void +~RabbitMQ.Client.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionHandlerAsync.get -> System.Func +~RabbitMQ.Client.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionHandlerAsync.set -> void +~static RabbitMQ.Client.IChannelExtensions.AbortAsync(this RabbitMQ.Client.IChannel channel) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IChannelExtensions.BasicConsumeAsync(this RabbitMQ.Client.IChannel channel, RabbitMQ.Client.IBasicConsumer consumer, string queue, bool autoAck = false, string consumerTag = "", bool noLocal = false, bool exclusive = false, System.Collections.Generic.IDictionary arguments = null) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IChannelExtensions.BasicConsumeAsync(this RabbitMQ.Client.IChannel channel, string queue, bool autoAck, RabbitMQ.Client.IBasicConsumer consumer) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IChannelExtensions.BasicConsumeAsync(this RabbitMQ.Client.IChannel channel, string queue, bool autoAck, string consumerTag, RabbitMQ.Client.IBasicConsumer consumer) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IChannelExtensions.BasicConsumeAsync(this RabbitMQ.Client.IChannel channel, string queue, bool autoAck, string consumerTag, System.Collections.Generic.IDictionary arguments, RabbitMQ.Client.IBasicConsumer consumer) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IChannelExtensions.CloseAsync(this RabbitMQ.Client.IChannel channel) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IChannelExtensions.CloseAsync(this RabbitMQ.Client.IChannel channel, ushort replyCode, string replyText) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IChannelExtensions.ExchangeDeclareAsync(this RabbitMQ.Client.IChannel channel, string exchange, string type, bool durable = false, bool autoDelete = false, System.Collections.Generic.IDictionary arguments = null, bool noWait = false) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IChannelExtensions.QueueDeclareAsync(this RabbitMQ.Client.IChannel channel, string queue = "", bool durable = false, bool exclusive = true, bool autoDelete = true, System.Collections.Generic.IDictionary arguments = null, bool noWait = false) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IChannelExtensions.QueueDeleteAsync(this RabbitMQ.Client.IChannel channel, string queue, bool ifUnused = false, bool ifEmpty = false) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IChannelExtensions.QueueUnbindAsync(this RabbitMQ.Client.IChannel channel, string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments = null) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IConnectionExtensions.AbortAsync(this RabbitMQ.Client.IConnection connection) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IConnectionExtensions.AbortAsync(this RabbitMQ.Client.IConnection connection, System.TimeSpan timeout) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IConnectionExtensions.AbortAsync(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IConnectionExtensions.AbortAsync(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText, System.TimeSpan timeout) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IConnectionExtensions.CloseAsync(this RabbitMQ.Client.IConnection connection) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IConnectionExtensions.CloseAsync(this RabbitMQ.Client.IConnection connection, System.TimeSpan timeout) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IConnectionExtensions.CloseAsync(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText) -> System.Threading.Tasks.Task +~static RabbitMQ.Client.IConnectionExtensions.CloseAsync(this RabbitMQ.Client.IConnection connection, ushort reasonCode, string reasonText, System.TimeSpan timeout) -> System.Threading.Tasks.Task +~virtual RabbitMQ.Client.DefaultBasicConsumer.HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, RabbitMQ.Client.ReadOnlyBasicProperties properties, System.ReadOnlyMemory body) -> System.Threading.Tasks.Task diff --git a/projects/RabbitMQ.Client/RabbitMQ.Client.csproj b/projects/RabbitMQ.Client/RabbitMQ.Client.csproj index 3ed292dfcd..e06dd89034 100644 --- a/projects/RabbitMQ.Client/RabbitMQ.Client.csproj +++ b/projects/RabbitMQ.Client/RabbitMQ.Client.csproj @@ -27,9 +27,13 @@ true ../../packages true - latest - 7.0 README.md + + 8.0 diff --git a/projects/RabbitMQ.Client/client/RentedMemory.cs b/projects/RabbitMQ.Client/client/RentedMemory.cs index b4679e3b6d..6f52bf832e 100644 --- a/projects/RabbitMQ.Client/client/RentedMemory.cs +++ b/projects/RabbitMQ.Client/client/RentedMemory.cs @@ -46,6 +46,7 @@ internal RentedMemory(ReadOnlyMemory memory, byte[] rentedArray) { Memory = memory; RentedArray = rentedArray; + _disposedValue = false; } internal readonly ReadOnlyMemory Memory; diff --git a/projects/RabbitMQ.Client/client/TaskExtensions.cs b/projects/RabbitMQ.Client/client/TaskExtensions.cs index 97eececaa3..b0328875af 100644 --- a/projects/RabbitMQ.Client/client/TaskExtensions.cs +++ b/projects/RabbitMQ.Client/client/TaskExtensions.cs @@ -37,6 +37,18 @@ namespace RabbitMQ.Client { internal static class TaskExtensions { +#if NET6_0_OR_GREATER + public static bool IsCompletedSuccessfully(this Task task) + { + return task.IsCompletedSuccessfully; + } +#else + public static bool IsCompletedSuccessfully(this Task task) + { + return task.Status == TaskStatus.RanToCompletion; + } +#endif + #if !NET6_0_OR_GREATER private static readonly TaskContinuationOptions s_tco = TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously; private static void IgnoreTaskContinuation(Task t, object s) => t.Exception.Handle(e => true); @@ -59,7 +71,7 @@ public static async Task WithCancellation(this Task task, CancellationToken canc } #endif - public static Task TimeoutAfter(this Task task, TimeSpan timeout) + public static Task WaitAsync(this Task task, TimeSpan timeout) { #if NET6_0_OR_GREATER if (task.IsCompletedSuccessfully) @@ -69,7 +81,7 @@ public static Task TimeoutAfter(this Task task, TimeSpan timeout) return task.WaitAsync(timeout); #else - if (task.Status == TaskStatus.RanToCompletion) + if (task.IsCompletedSuccessfully()) { return task; } diff --git a/projects/RabbitMQ.Client/client/api/AsyncDefaultBasicConsumer.cs b/projects/RabbitMQ.Client/client/api/AsyncDefaultBasicConsumer.cs index a7a0fe4035..64fb982029 100644 --- a/projects/RabbitMQ.Client/client/api/AsyncDefaultBasicConsumer.cs +++ b/projects/RabbitMQ.Client/client/api/AsyncDefaultBasicConsumer.cs @@ -29,7 +29,7 @@ public AsyncDefaultBasicConsumer(IChannel channel) /// /// Retrieve the consumer tags this consumer is registered as; to be used when discussing this consumer - /// with the server, for instance with . + /// with the server, for instance with . /// public string[] ConsumerTags { @@ -101,7 +101,7 @@ public virtual Task HandleBasicConsumeOk(string consumerTag) /// Called each time a message is delivered for this consumer. /// /// - /// This is a no-op implementation. It will not acknowledge deliveries via + /// This is a no-op implementation. It will not acknowledge deliveries via /// if consuming in automatic acknowledgement mode. /// Subclasses must copy or fully use delivery body before returning. /// Accessing the body at a later point is unsafe as its memory can @@ -166,8 +166,8 @@ void IBasicConsumer.HandleBasicConsumeOk(string consumerTag) throw new InvalidOperationException("Should never be called. Enable 'DispatchConsumersAsync'."); } - void IBasicConsumer.HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - in ReadOnlyBasicProperties properties, ReadOnlyMemory body) + Task IBasicConsumer.HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, + ReadOnlyBasicProperties properties, ReadOnlyMemory body) { throw new InvalidOperationException("Should never be called. Enable 'DispatchConsumersAsync'."); } diff --git a/projects/RabbitMQ.Client/client/api/BasicGetResult.cs b/projects/RabbitMQ.Client/client/api/BasicGetResult.cs index 64a23a5f1f..aef3d51750 100644 --- a/projects/RabbitMQ.Client/client/api/BasicGetResult.cs +++ b/projects/RabbitMQ.Client/client/api/BasicGetResult.cs @@ -72,7 +72,7 @@ public sealed class BasicGetResult public readonly ReadOnlyMemory Body; /// - /// Retrieve the delivery tag for this message. See also . + /// Retrieve the delivery tag for this message. See also . /// public readonly ulong DeliveryTag; diff --git a/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs b/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs index 7a50ae9e85..3cc437b4e4 100644 --- a/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs +++ b/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs @@ -403,19 +403,6 @@ public IAuthMechanismFactory AuthMechanismFactory(IEnumerable argServerM return null; } - /// - /// Create a connection to one of the endpoints provided by the IEndpointResolver - /// returned by the EndpointResolverFactory. By default the configured - /// hostname and port are used. - /// - /// - /// When the configured hostname was not reachable. - /// - public IConnection CreateConnection() - { - return CreateConnection(ClientProvidedName); - } - /// /// Asynchronously reate a connection to one of the endpoints provided by the IEndpointResolver /// returned by the EndpointResolverFactory. By default the configured @@ -425,31 +412,12 @@ public IConnection CreateConnection() /// /// When the configured hostname was not reachable. /// - public ValueTask CreateConnectionAsync( + public Task CreateConnectionAsync( CancellationToken cancellationToken = default) { return CreateConnectionAsync(ClientProvidedName, cancellationToken); } - /// - /// Create a connection to one of the endpoints provided by the IEndpointResolver - /// returned by the EndpointResolverFactory. By default the configured - /// hostname and port are used. - /// - /// - /// Application-specific connection name, will be displayed in the management UI - /// if RabbitMQ server supports it. This value doesn't have to be unique and cannot - /// be used as a connection identifier, e.g. in HTTP API requests. - /// This value is supposed to be human-readable. - /// - /// - /// When the configured hostname was not reachable. - /// - public IConnection CreateConnection(string clientProvidedName) - { - return CreateConnection(EndpointResolverFactory(LocalEndpoints()), clientProvidedName); - } - /// /// Asynchronously create a connection to one of the endpoints provided by the IEndpointResolver /// returned by the EndpointResolverFactory. By default the configured @@ -465,31 +433,12 @@ public IConnection CreateConnection(string clientProvidedName) /// /// When the configured hostname was not reachable. /// - public ValueTask CreateConnectionAsync(string clientProvidedName, + public Task CreateConnectionAsync(string clientProvidedName, CancellationToken cancellationToken = default) { return CreateConnectionAsync(EndpointResolverFactory(LocalEndpoints()), clientProvidedName, cancellationToken); } - /// - /// Create a connection using a list of hostnames using the configured port. - /// By default each hostname is tried in a random order until a successful connection is - /// found or the list is exhausted using the DefaultEndpointResolver. - /// The selection behaviour can be overridden by configuring the EndpointResolverFactory. - /// - /// - /// List of hostnames to use for the initial - /// connection and recovery. - /// - /// Open connection - /// - /// When no hostname was reachable. - /// - public IConnection CreateConnection(IEnumerable hostnames) - { - return CreateConnection(hostnames, ClientProvidedName); - } - /// /// Asynchronously create a connection using a list of hostnames using the configured port. /// By default each hostname is tried in a random order until a successful connection is @@ -505,38 +454,12 @@ public IConnection CreateConnection(IEnumerable hostnames) /// /// When no hostname was reachable. /// - public ValueTask CreateConnectionAsync(IEnumerable hostnames, + public Task CreateConnectionAsync(IEnumerable hostnames, CancellationToken cancellationToken = default) { return CreateConnectionAsync(hostnames, ClientProvidedName, cancellationToken); } - /// - /// Create a connection using a list of hostnames using the configured port. - /// By default each endpoint is tried in a random order until a successful connection is - /// found or the list is exhausted. - /// The selection behaviour can be overridden by configuring the EndpointResolverFactory. - /// - /// - /// List of hostnames to use for the initial - /// connection and recovery. - /// - /// - /// Application-specific connection name, will be displayed in the management UI - /// if RabbitMQ server supports it. This value doesn't have to be unique and cannot - /// be used as a connection identifier, e.g. in HTTP API requests. - /// This value is supposed to be human-readable. - /// - /// Open connection - /// - /// When no hostname was reachable. - /// - public IConnection CreateConnection(IEnumerable hostnames, string clientProvidedName) - { - IEnumerable endpoints = hostnames.Select(h => new AmqpTcpEndpoint(h, Port, Ssl, MaxMessageSize)); - return CreateConnection(EndpointResolverFactory(endpoints), clientProvidedName); - } - /// /// Asynchronously create a connection using a list of hostnames using the configured port. /// By default each endpoint is tried in a random order until a successful connection is @@ -558,31 +481,13 @@ public IConnection CreateConnection(IEnumerable hostnames, string client /// /// When no hostname was reachable. /// - public ValueTask CreateConnectionAsync(IEnumerable hostnames, string clientProvidedName, + public Task CreateConnectionAsync(IEnumerable hostnames, string clientProvidedName, CancellationToken cancellationToken = default) { IEnumerable endpoints = hostnames.Select(h => new AmqpTcpEndpoint(h, Port, Ssl, MaxMessageSize)); return CreateConnectionAsync(EndpointResolverFactory(endpoints), clientProvidedName, cancellationToken); } - /// - /// Create a connection using a list of endpoints. By default each endpoint will be tried - /// in a random order until a successful connection is found or the list is exhausted. - /// The selection behaviour can be overridden by configuring the EndpointResolverFactory. - /// - /// - /// List of endpoints to use for the initial - /// connection and recovery. - /// - /// Open connection - /// - /// When no hostname was reachable. - /// - public IConnection CreateConnection(IEnumerable endpoints) - { - return CreateConnection(endpoints, ClientProvidedName); - } - /// /// Asynchronously create a connection using a list of endpoints. By default each endpoint will be tried /// in a random order until a successful connection is found or the list is exhausted. @@ -597,36 +502,12 @@ public IConnection CreateConnection(IEnumerable endpoints) /// /// When no hostname was reachable. /// - public ValueTask CreateConnectionAsync(IEnumerable endpoints, + public Task CreateConnectionAsync(IEnumerable endpoints, CancellationToken cancellationToken = default) { return CreateConnectionAsync(endpoints, ClientProvidedName, cancellationToken); } - /// - /// Create a connection using a list of endpoints. By default each endpoint will be tried - /// in a random order until a successful connection is found or the list is exhausted. - /// The selection behaviour can be overridden by configuring the EndpointResolverFactory. - /// - /// - /// List of endpoints to use for the initial - /// connection and recovery. - /// - /// - /// Application-specific connection name, will be displayed in the management UI - /// if RabbitMQ server supports it. This value doesn't have to be unique and cannot - /// be used as a connection identifier, e.g. in HTTP API requests. - /// This value is supposed to be human-readable. - /// - /// Open connection - /// - /// When no hostname was reachable. - /// - public IConnection CreateConnection(IEnumerable endpoints, string clientProvidedName) - { - return CreateConnection(EndpointResolverFactory(endpoints), clientProvidedName); - } - /// /// Asynchronously create a connection using a list of endpoints. By default each endpoint will be tried /// in a random order until a successful connection is found or the list is exhausted. @@ -647,52 +528,12 @@ public IConnection CreateConnection(IEnumerable endpoints, stri /// /// When no hostname was reachable. /// - public ValueTask CreateConnectionAsync(IEnumerable endpoints, string clientProvidedName, + public Task CreateConnectionAsync(IEnumerable endpoints, string clientProvidedName, CancellationToken cancellationToken = default) { return CreateConnectionAsync(EndpointResolverFactory(endpoints), clientProvidedName, cancellationToken); } - /// - /// Create a connection using an IEndpointResolver. - /// - /// - /// The endpointResolver that returns the endpoints to use for the connection attempt. - /// - /// - /// Application-specific connection name, will be displayed in the management UI - /// if RabbitMQ server supports it. This value doesn't have to be unique and cannot - /// be used as a connection identifier, e.g. in HTTP API requests. - /// This value is supposed to be human-readable. - /// - /// Open connection - /// - /// When no hostname was reachable. - /// - public IConnection CreateConnection(IEndpointResolver endpointResolver, string clientProvidedName) - { - ConnectionConfig config = CreateConfig(clientProvidedName); - try - { - if (AutomaticRecoveryEnabled) - { - var c = new AutorecoveringConnection(config, endpointResolver); - return (AutorecoveringConnection)c.Open(); - } - else - { - IFrameHandler frameHandler = endpointResolver.SelectOneAsync( - CreateFrameHandlerAsync, CancellationToken.None).EnsureCompleted(); - var c = new Connection(config, frameHandler); - return (Connection)c.Open(); - } - } - catch (Exception ex) - { - throw new BrokerUnreachableException(ex); - } - } - /// /// Asynchronously create a connection using an IEndpointResolver. /// @@ -710,7 +551,7 @@ public IConnection CreateConnection(IEndpointResolver endpointResolver, string c /// /// When no hostname was reachable. /// - public async ValueTask CreateConnectionAsync(IEndpointResolver endpointResolver, string clientProvidedName, + public async Task CreateConnectionAsync(IEndpointResolver endpointResolver, string clientProvidedName, CancellationToken cancellationToken = default) { ConnectionConfig config = CreateConfig(clientProvidedName); diff --git a/projects/RabbitMQ.Client/client/api/DefaultBasicConsumer.cs b/projects/RabbitMQ.Client/client/api/DefaultBasicConsumer.cs index 15b40ea808..6d7e6c5b9a 100644 --- a/projects/RabbitMQ.Client/client/api/DefaultBasicConsumer.cs +++ b/projects/RabbitMQ.Client/client/api/DefaultBasicConsumer.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using RabbitMQ.Client.Events; using RabbitMQ.Client.Impl; @@ -39,7 +40,7 @@ namespace RabbitMQ.Client { /// /// Useful default/base implementation of . - /// Subclass and override in application code. + /// Subclass and override in application code. /// /// /// Note that the "Handle*" methods run in the connection's thread! @@ -68,7 +69,7 @@ public DefaultBasicConsumer(IChannel channel) /// /// Retrieve the consumer tags this consumer is registered as; to be used to identify - /// this consumer, for example, when cancelling it with . + /// this consumer, for example, when cancelling it with . /// This value is an array because a single consumer instance can be reused to consume on /// multiple channels. /// @@ -141,21 +142,22 @@ public virtual void HandleBasicConsumeOk(string consumerTag) /// Called each time a message is delivered for this consumer. /// /// - /// This is a no-op implementation. It will not acknowledge deliveries via + /// This is a no-op implementation. It will not acknowledge deliveries via /// if consuming in automatic acknowledgement mode. /// Subclasses must copy or fully use delivery body before returning. /// Accessing the body at a later point is unsafe as its memory can /// be already released. /// - public virtual void HandleBasicDeliver(string consumerTag, + public virtual Task HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - in ReadOnlyBasicProperties properties, + ReadOnlyBasicProperties properties, ReadOnlyMemory body) { // Nothing to do here. + return Task.CompletedTask; } /// diff --git a/projects/RabbitMQ.Client/client/api/IAsyncBasicConsumer.cs b/projects/RabbitMQ.Client/client/api/IAsyncBasicConsumer.cs index 291eda1293..2a3c2d1ce1 100644 --- a/projects/RabbitMQ.Client/client/api/IAsyncBasicConsumer.cs +++ b/projects/RabbitMQ.Client/client/api/IAsyncBasicConsumer.cs @@ -43,7 +43,7 @@ public interface IAsyncBasicConsumer /// /// /// Does nothing with the passed in information. - /// Note that in particular, some delivered messages may require acknowledgement via . + /// Note that in particular, some delivered messages may require acknowledgement via . /// The implementation of this method in this class does NOT acknowledge such messages. /// Task HandleBasicDeliver(string consumerTag, diff --git a/projects/RabbitMQ.Client/client/api/IBasicConsumer.cs b/projects/RabbitMQ.Client/client/api/IBasicConsumer.cs index d5244e973e..7c796911db 100644 --- a/projects/RabbitMQ.Client/client/api/IBasicConsumer.cs +++ b/projects/RabbitMQ.Client/client/api/IBasicConsumer.cs @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------- using System; - +using System.Threading.Tasks; using RabbitMQ.Client.Events; namespace RabbitMQ.Client @@ -86,15 +86,15 @@ public interface IBasicConsumer /// /// /// Does nothing with the passed in information. - /// Note that in particular, some delivered messages may require acknowledgement via . + /// Note that in particular, some delivered messages may require acknowledgement via . /// The implementation of this method in this class does NOT acknowledge such messages. /// - void HandleBasicDeliver(string consumerTag, + Task HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - in ReadOnlyBasicProperties properties, + ReadOnlyBasicProperties properties, ReadOnlyMemory body); /// diff --git a/projects/RabbitMQ.Client/client/api/IChannel.cs b/projects/RabbitMQ.Client/client/api/IChannel.cs index 579ca518c4..5a98e65837 100644 --- a/projects/RabbitMQ.Client/client/api/IChannel.cs +++ b/projects/RabbitMQ.Client/client/api/IChannel.cs @@ -143,41 +143,15 @@ public interface IChannel : IDisposable /// event EventHandler ChannelShutdown; - /// Acknknowledges one or more messages. - /// The delivery tag. - /// Ack all messages up to the delivery tag if set to true. - void BasicAck(ulong deliveryTag, bool multiple); - /// Asynchronously acknknowledges one or more messages. /// The delivery tag. /// Ack all messages up to the delivery tag if set to true. ValueTask BasicAckAsync(ulong deliveryTag, bool multiple); - /// Cancel a Basic content-class consumer. - /// The consumer tag. - void BasicCancel(string consumerTag); - /// Asynchronously cancel a Basic content-class consumer. /// The consumer tag. - ValueTask BasicCancelAsync(string consumerTag); - - /// - /// Same as BasicCancel but sets nowait to true and returns void (as there - /// will be no response from the server). - /// - /// The consumer tag. - void BasicCancelNoWait(string consumerTag); - - /// Start a Basic content-class consumer. - /// The queue. - /// If set to true, automatically ack messages. - /// The consumer tag. - /// If set to true, this consumer will not receive messages published by the same connection. - /// If set to true, the consumer is exclusive. - /// Consumer arguments. - /// The consumer, an instance of - /// - string BasicConsume(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, IDictionary arguments, IBasicConsumer consumer); + /// If set to true, do not require a response from the server. + Task BasicCancelAsync(string consumerTag, bool noWait = false); /// Asynchronously start a Basic content-class consumer. /// The queue. @@ -188,17 +162,7 @@ public interface IChannel : IDisposable /// Consumer arguments. /// The consumer, an instance of /// - ValueTask BasicConsumeAsync(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, IDictionary arguments, IBasicConsumer consumer); - - /// - /// Retrieve an individual message, if - /// one is available; returns null if the server answers that - /// no messages are currently available. See also . - /// - /// The queue. - /// If set to true, automatically ack the message. - /// - BasicGetResult BasicGet(string queue, bool autoAck); + Task BasicConsumeAsync(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, IDictionary arguments, IBasicConsumer consumer); /// /// Asynchronously retrieve an individual message, if @@ -210,14 +174,6 @@ public interface IChannel : IDisposable /// ValueTask BasicGetAsync(string queue, bool autoAck); - /// - /// Nack one or more delivered message(s). - /// - /// The delivery tag. - /// If set to true, nack all messages up to the current tag. - /// If set to true, requeue nack'd messages. - void BasicNack(ulong deliveryTag, bool multiple, bool requeue); - /// /// Asynchronously nack one or more delivered message(s). /// @@ -228,28 +184,6 @@ public interface IChannel : IDisposable #nullable enable - /// - /// Publishes a message. - /// - /// - /// - /// Routing key must be shorter than 255 bytes. - /// - /// - void BasicPublish(string exchange, string routingKey, in TProperties basicProperties, ReadOnlyMemory body = default, bool mandatory = false) - where TProperties : IReadOnlyBasicProperties, IAmqpHeader; - - /// - /// Publishes a message. - /// - /// - /// - /// Routing key must be shorter than 255 bytes. - /// - /// - void BasicPublish(CachedString exchange, CachedString routingKey, in TProperties basicProperties, ReadOnlyMemory body = default, bool mandatory = false) - where TProperties : IReadOnlyBasicProperties, IAmqpHeader; - /// /// Asynchronously publishes a message. /// @@ -281,28 +215,10 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// The prefetch count. /// If set to true, use global prefetch. /// See the Consumer Prefetch documentation. - void BasicQos(uint prefetchSize, ushort prefetchCount, bool global); - - /// - /// Configures QoS parameters of the Basic content-class. - /// - /// Size of the prefetch in bytes. - /// The prefetch count. - /// If set to true, use global prefetch. - /// See the Consumer Prefetch documentation. - ValueTask BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global); - - /// Reject a delivered message. - void BasicReject(ulong deliveryTag, bool requeue); + Task BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global); /// Reject a delivered message. - ValueTask BasicRejectAsync(ulong deliveryTag, bool requeue); - - /// Close this session. - /// The reply code to send for closing (See under "Reply Codes" in the AMQP specification). - /// The reply text to send for closing. - /// Whether or not the close is an abort (ignoring certain exceptions). - void Close(ushort replyCode, string replyText, bool abort); + Task BasicRejectAsync(ulong deliveryTag, bool requeue); /// /// Asynchronously close this session. @@ -310,7 +226,7 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// The reply code to send for closing (See under "Reply Codes" in the AMQP specification). /// The reply text to send for closing. /// Whether or not the close is an abort (ignoring certain exceptions). - ValueTask CloseAsync(ushort replyCode, string replyText, bool abort); + Task CloseAsync(ushort replyCode, string replyText, bool abort); /// /// Asynchronously close this session. @@ -318,23 +234,10 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// The instance containing the close data. /// Whether or not the close is an abort (ignoring certain exceptions). /// - ValueTask CloseAsync(ShutdownEventArgs reason, bool abort); - - /// Enable publisher confirmations. - void ConfirmSelect(); + Task CloseAsync(ShutdownEventArgs reason, bool abort); /// Asynchronously enable publisher confirmations. - ValueTask ConfirmSelectAsync(); - - /// - /// Bind an exchange to an exchange. - /// - /// - /// - /// Routing key must be shorter than 255 bytes. - /// - /// - void ExchangeBind(string destination, string source, string routingKey, IDictionary arguments); + Task ConfirmSelectAsync(); /// /// Asynchronously binds an exchange to an exchange. @@ -344,40 +247,18 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// Routing key must be shorter than 255 bytes. /// /// - ValueTask ExchangeBindAsync(string destination, string source, string routingKey, IDictionary arguments); - - /// - /// Like ExchangeBind but sets nowait to true. - /// - /// - /// - /// Routing key must be shorter than 255 bytes. - /// - /// - void ExchangeBindNoWait(string destination, string source, string routingKey, IDictionary arguments); - - /// Declare an exchange. - /// - /// The exchange is declared non-passive and non-internal. - /// The "nowait" option is not used. - /// - void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments); + Task ExchangeBindAsync(string destination, string source, string routingKey, + IDictionary arguments = null, bool noWait = false); /// Asynchronously declare an exchange. /// /// The exchange is declared non-internal. - /// The "nowait" option is not used. /// - ValueTask ExchangeDeclareAsync(string exchange, string type, bool passive, bool durable, bool autoDelete, IDictionary arguments); + Task ExchangeDeclareAsync(string exchange, string type, bool durable, bool autoDelete, + IDictionary arguments = null, bool passive = false, bool noWait = false); /// - /// Same as ExchangeDeclare but sets nowait to true and returns void (as there - /// will be no response from the server). - /// - void ExchangeDeclareNoWait(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments); - - /// - /// Do a passive exchange declaration. + /// Asynchronously do a passive exchange declaration. /// /// /// This method performs a "passive declare" on an exchange, @@ -385,30 +266,12 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// It will do nothing if the exchange already exists and result /// in a channel-level protocol exception (channel closure) if not. /// - void ExchangeDeclarePassive(string exchange); - - /// - /// Delete an exchange. - /// - void ExchangeDelete(string exchange, bool ifUnused); + Task ExchangeDeclarePassiveAsync(string exchange); /// /// Asynchronously delete an exchange. /// - ValueTask ExchangeDeleteAsync(string exchange, bool ifUnused); - - /// - /// Like ExchangeDelete but sets nowait to true. - /// - void ExchangeDeleteNoWait(string exchange, bool ifUnused); - - /// - /// Unbind an exchange from an exchange. - /// - /// - /// Routing key must be shorter than 255 bytes. - /// - void ExchangeUnbind(string destination, string source, string routingKey, IDictionary arguments); + Task ExchangeDeleteAsync(string exchange, bool ifUnused = false, bool noWait = false); /// /// Asynchronously unbind an exchange from an exchange. @@ -416,29 +279,8 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// /// Routing key must be shorter than 255 bytes. /// - ValueTask ExchangeUnbindAsync(string destination, string source, string routingKey, IDictionary arguments); - - /// - /// Like ExchangeUnbind but sets nowait to true. - /// - /// - /// - /// Routing key must be shorter than 255 bytes. - /// - /// - void ExchangeUnbindNoWait(string destination, string source, string routingKey, IDictionary arguments); - - /// - /// Bind a queue to an exchange. - /// - /// The queue. - /// The exchange. - /// The routing key. - /// The arguments. - /// - /// Routing key must be shorter than 255 bytes. - /// - void QueueBind(string queue, string exchange, string routingKey, IDictionary arguments); + Task ExchangeUnbindAsync(string destination, string source, string routingKey, + IDictionary arguments = null, bool noWait = false); /// /// Asynchronously bind a queue to an exchange. @@ -447,57 +289,33 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// The exchange. /// The routing key. /// The arguments. + /// If set to true, do not require a response from the server. /// /// Routing key must be shorter than 255 bytes. /// - ValueTask QueueBindAsync(string queue, string exchange, string routingKey, IDictionary arguments); - - /// Same as QueueBind but sets nowait parameter to true. - /// - /// - /// Routing key must be shorter than 255 bytes. - /// - /// - void QueueBindNoWait(string queue, string exchange, string routingKey, IDictionary arguments); - - /// - /// Declares a queue. See the Queues guide to learn more. - /// - /// The name of the queue. Pass an empty string to make the server generate a name. - /// Should this queue will survive a broker restart? - /// Should this queue use be limited to its declaring connection? Such a queue will be deleted when its declaring connection closes. - /// Should this queue be auto-deleted when its last consumer (if any) unsubscribes? - /// Optional; additional queue arguments, e.g. "x-queue-type" - QueueDeclareOk QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments); + Task QueueBindAsync(string queue, string exchange, string routingKey, + IDictionary arguments = null, bool noWait = false); /// /// Asynchronously declares a queue. See the Queues guide to learn more. /// /// The name of the queue. Pass an empty string to make the server generate a name. - /// Set to true to passively declare the queue (i.e. check for its existence) /// Should this queue will survive a broker restart? /// Should this queue use be limited to its declaring connection? Such a queue will be deleted when its declaring connection closes. /// Should this queue be auto-deleted when its last consumer (if any) unsubscribes? /// Optional; additional queue arguments, e.g. "x-queue-type" - ValueTask QueueDeclareAsync(string queue, bool passive, bool durable, bool exclusive, bool autoDelete, IDictionary arguments); + /// Optional; Set to true to passively declare the queue (i.e. check for its existence) + /// Optional; Set to true to not require a response from the server. + Task QueueDeclareAsync(string queue, bool durable, bool exclusive, bool autoDelete, + IDictionary arguments = null, bool passive = false, bool noWait = false); - /// - /// Declares a queue. See the Queues guide to learn more. - /// - /// The name of the queue. Pass an empty string to make the server generate a name. - /// Should this queue will survive a broker restart? - /// Should this queue use be limited to its declaring connection? Such a queue will be deleted when its declaring connection closes. - /// Should this queue be auto-deleted when its last consumer (if any) unsubscribes? - /// Optional; additional queue arguments, e.g. "x-queue-type" - void QueueDeclareNoWait(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments); - - /// Declare a queue passively. + /// Asynchronously declare a queue passively. /// ///The queue is declared passive, non-durable, ///non-exclusive, and non-autodelete, with no arguments. ///The queue is declared passively; i.e. only check if it exists. /// - QueueDeclareOk QueueDeclarePassive(string queue); + Task QueueDeclarePassiveAsync(string queue); /// /// Returns the number of messages in a queue ready to be delivered @@ -505,7 +323,7 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// an exception will be closed with an exception. /// /// The name of the queue - uint MessageCount(string queue); + Task MessageCountAsync(string queue); /// /// Returns the number of consumers on a queue. @@ -513,56 +331,24 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// an exception will be closed with an exception. /// /// The name of the queue - uint ConsumerCount(string queue); + Task ConsumerCountAsync(string queue); /// - /// Deletes a queue. See the Queues guide to learn more. + /// Asynchronously deletes a queue. See the Queues guide to learn more. /// /// The name of the queue. /// Only delete the queue if it is unused. /// Only delete the queue if it is empty. - /// Returns the number of messages purged during deletion. - uint QueueDelete(string queue, bool ifUnused, bool ifEmpty); - - /// - /// Asynchronously deletes a queue. See the Queues guide to learn more. - /// + /// If set to true, do not require a response from the server. /// ///Returns the number of messages purged during queue deletion. /// - ValueTask QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty); - - /// - ///Same as QueueDelete but sets nowait parameter to true - ///and returns void (as there will be no response from the server) - /// - /// The name of the queue. - /// Only delete the queue if it is unused. - /// Only delete the queue if it is empty. - /// Returns the number of messages purged during deletion. - void QueueDeleteNoWait(string queue, bool ifUnused, bool ifEmpty); - - /// Asynchronously purge a queue of messages. - /// The queue. - /// Returns the number of messages purged. - uint QueuePurge(string queue); + Task QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty, bool noWait = false); /// Asynchronously purge a queue of messages. /// The queue. /// Returns the number of messages purged. - ValueTask QueuePurgeAsync(string queue); - - /// - /// Unbind a queue from an exchange. - /// - /// The queue. - /// The exchange. - /// The routing key. - /// The arguments. - /// - /// Routing key must be shorter than 255 bytes. - /// - void QueueUnbind(string queue, string exchange, string routingKey, IDictionary arguments); + Task QueuePurgeAsync(string queue); /// /// Asynchronously unbind a queue from an exchange. @@ -574,38 +360,16 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// /// Routing key must be shorter than 255 bytes. /// - ValueTask QueueUnbindAsync(string queue, string exchange, string routingKey, IDictionary arguments); - - /// Commit this session's active TX transaction. - void TxCommit(); + Task QueueUnbindAsync(string queue, string exchange, string routingKey, IDictionary arguments); /// Asynchronously commit this session's active TX transaction. - ValueTask TxCommitAsync(); - - /// Roll back this session's active TX transaction. - void TxRollback(); + Task TxCommitAsync(); /// Asynchronously roll back this session's active TX transaction. - ValueTask TxRollbackAsync(); - - /// Enable TX mode for this session. - void TxSelect(); + Task TxRollbackAsync(); /// Asynchronously enable TX mode for this session. - ValueTask TxSelectAsync(); - - /// - /// Wait until all published messages on this channel have been confirmed. - /// - /// True if no nacks were received within the timeout, otherwise false. - /// - /// Waits until all messages published on this channel since the last call have - /// been either ack'd or nack'd by the server. Returns whether - /// all the messages were ack'd (and none were nack'd). - /// Throws an exception when called on a channel - /// that does not have publisher confirms enabled. - /// - bool WaitForConfirms(); + Task TxSelectAsync(); /// /// Asynchronously wait until all published messages on this channel have been confirmed. @@ -621,17 +385,6 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou /// Task WaitForConfirmsAsync(CancellationToken token = default); - /// - /// Wait until all published messages on this channel have been confirmed. - /// - /// - /// Waits until all messages published on this channel since the last call have - /// been ack'd by the server. If a nack is received or the timeout - /// elapses, throws an IOException exception immediately and closes - /// the channel. - /// - void WaitForConfirmsOrDie(); - /// /// Wait until all published messages on this channel have been confirmed. /// diff --git a/projects/RabbitMQ.Client/client/api/IChannelExtensions.cs b/projects/RabbitMQ.Client/client/api/IChannelExtensions.cs index 74edd17fb2..862035fa9b 100644 --- a/projects/RabbitMQ.Client/client/api/IChannelExtensions.cs +++ b/projects/RabbitMQ.Client/client/api/IChannelExtensions.cs @@ -38,21 +38,8 @@ namespace RabbitMQ.Client { public static class IChannelExtensions { - /// Start a Basic content-class consumer. - public static string BasicConsume(this IChannel channel, - IBasicConsumer consumer, - string queue, - bool autoAck = false, - string consumerTag = "", - bool noLocal = false, - bool exclusive = false, - IDictionary arguments = null) - { - return channel.BasicConsume(queue, autoAck, consumerTag, noLocal, exclusive, arguments, consumer); - } - /// Asynchronously start a Basic content-class consumer. - public static ValueTask BasicConsumeAsync(this IChannel channel, + public static Task BasicConsumeAsync(this IChannel channel, IBasicConsumer consumer, string queue, bool autoAck = false, @@ -64,33 +51,16 @@ public static class IChannelExtensions return channel.BasicConsumeAsync(queue, autoAck, consumerTag, noLocal, exclusive, arguments, consumer); } - /// Start a Basic content-class consumer. - public static string BasicConsume(this IChannel channel, string queue, - bool autoAck, - IBasicConsumer consumer) - { - return channel.BasicConsume(queue, autoAck, string.Empty, false, false, null, consumer); - } - /// Asynchronously start a Basic content-class consumer. - public static ValueTask BasicConsumeAsync(this IChannel channel, string queue, + public static Task BasicConsumeAsync(this IChannel channel, string queue, bool autoAck, IBasicConsumer consumer) { return channel.BasicConsumeAsync(queue, autoAck, string.Empty, false, false, null, consumer); } - /// Start a Basic content-class consumer. - public static string BasicConsume(this IChannel channel, string queue, - bool autoAck, - string consumerTag, - IBasicConsumer consumer) - { - return channel.BasicConsume(queue, autoAck, consumerTag, false, false, null, consumer); - } - /// Asynchronously start a Basic content-class consumer. - public static ValueTask BasicConsumeAsync(this IChannel channel, string queue, + public static Task BasicConsumeAsync(this IChannel channel, string queue, bool autoAck, string consumerTag, IBasicConsumer consumer) @@ -98,18 +68,8 @@ public static class IChannelExtensions return channel.BasicConsumeAsync(queue, autoAck, consumerTag, false, false, null, consumer); } - /// Start a Basic content-class consumer. - public static string BasicConsume(this IChannel channel, string queue, - bool autoAck, - string consumerTag, - IDictionary arguments, - IBasicConsumer consumer) - { - return channel.BasicConsume(queue, autoAck, consumerTag, false, false, arguments, consumer); - } - /// Asynchronously start a Basic content-class consumer. - public static ValueTask BasicConsumeAsync(this IChannel channel, string queue, + public static Task BasicConsumeAsync(this IChannel channel, string queue, bool autoAck, string consumerTag, IDictionary arguments, @@ -119,225 +79,64 @@ public static class IChannelExtensions } #nullable enable + /// /// (Extension method) Convenience overload of BasicPublish. /// /// /// The publication occurs with mandatory=false and immediate=false. /// - public static void BasicPublish(this IChannel channel, PublicationAddress addr, in T basicProperties, ReadOnlyMemory body) - where T : IReadOnlyBasicProperties, IAmqpHeader - { - channel.BasicPublish(addr.ExchangeName, addr.RoutingKey, in basicProperties, body); - } - public static ValueTask BasicPublishAsync(this IChannel channel, PublicationAddress addr, in T basicProperties, ReadOnlyMemory body) where T : IReadOnlyBasicProperties, IAmqpHeader { return channel.BasicPublishAsync(addr.ExchangeName, addr.RoutingKey, in basicProperties, body); } - public static void BasicPublish(this IChannel channel, string exchange, string routingKey, ReadOnlyMemory body = default, bool mandatory = false) - => channel.BasicPublish(exchange, routingKey, in EmptyBasicProperty.Empty, body, mandatory); - public static ValueTask BasicPublishAsync(this IChannel channel, string exchange, string routingKey, ReadOnlyMemory body = default, bool mandatory = false) => channel.BasicPublishAsync(exchange, routingKey, in EmptyBasicProperty.Empty, body, mandatory); - public static void BasicPublish(this IChannel channel, CachedString exchange, CachedString routingKey, ReadOnlyMemory body = default, bool mandatory = false) - => channel.BasicPublish(exchange, routingKey, in EmptyBasicProperty.Empty, body, mandatory); - public static ValueTask BasicPublishAsync(this IChannel channel, CachedString exchange, CachedString routingKey, ReadOnlyMemory body = default, bool mandatory = false) => channel.BasicPublishAsync(exchange, routingKey, in EmptyBasicProperty.Empty, body, mandatory); -#nullable disable - /// - /// Declare a queue. - /// - public static QueueDeclareOk QueueDeclare(this IChannel channel, string queue = "", bool durable = false, bool exclusive = true, - bool autoDelete = true, IDictionary arguments = null) - { - return channel.QueueDeclare(queue, durable, exclusive, autoDelete, arguments); - } +#nullable disable /// /// Asynchronously declare a queue. /// - public static ValueTask QueueDeclareAsync(this IChannel channel, string queue = "", bool durable = false, bool exclusive = true, - bool autoDelete = true, IDictionary arguments = null) + public static Task QueueDeclareAsync(this IChannel channel, string queue = "", bool durable = false, bool exclusive = true, + bool autoDelete = true, IDictionary arguments = null, bool noWait = false) { return channel.QueueDeclareAsync(queue: queue, passive: false, - durable: durable, exclusive: exclusive, autoDelete: autoDelete, arguments: arguments); - } - - /// - /// Bind an exchange to an exchange. - /// - public static void ExchangeBind(this IChannel channel, string destination, string source, string routingKey, IDictionary arguments = null) - { - channel.ExchangeBind(destination, source, routingKey, arguments); - } - - /// - /// Asynchronously bind an exchange to an exchange. - /// - public static ValueTask ExchangeBindAsync(this IChannel channel, string destination, string source, string routingKey, IDictionary arguments = null) - { - return channel.ExchangeBindAsync(destination, source, routingKey, arguments); - } - - /// - /// Like exchange bind but sets nowait to true. - /// - public static void ExchangeBindNoWait(this IChannel channel, string destination, string source, string routingKey, IDictionary arguments = null) - { - channel.ExchangeBindNoWait(destination, source, routingKey, arguments); - } - - /// - /// Declare an exchange. - /// - public static void ExchangeDeclare(this IChannel channel, string exchange, string type, bool durable = false, bool autoDelete = false, - IDictionary arguments = null) - { - channel.ExchangeDeclare(exchange, type, durable, autoDelete, arguments); + durable: durable, exclusive: exclusive, autoDelete: autoDelete, + arguments: arguments, noWait: noWait); } /// /// Asynchronously declare an exchange. /// - public static ValueTask ExchangeDeclareAsync(this IChannel channel, string exchange, string type, bool durable = false, bool autoDelete = false, - IDictionary arguments = null) + public static Task ExchangeDeclareAsync(this IChannel channel, string exchange, string type, bool durable = false, bool autoDelete = false, + IDictionary arguments = null, bool noWait = false) { - return channel.ExchangeDeclareAsync(exchange, type, false, durable, autoDelete, arguments); - } - - /// - /// Like ExchangeDeclare but sets nowait to true. - /// - public static void ExchangeDeclareNoWait(this IChannel channel, string exchange, string type, bool durable = false, bool autoDelete = false, - IDictionary arguments = null) - { - channel.ExchangeDeclareNoWait(exchange, type, durable, autoDelete, arguments); - } - - /// - /// Unbinds an exchange. - /// - public static void ExchangeUnbind(this IChannel channel, string destination, - string source, - string routingKey, - IDictionary arguments = null) - { - channel.ExchangeUnbind(destination, source, routingKey, arguments); - } - - /// - /// Asynchronously unbinds an exchange. - /// - public static ValueTask ExchangeUnbindAsync(this IChannel channel, string destination, - string source, - string routingKey, - IDictionary arguments = null) - { - return channel.ExchangeUnbindAsync(destination, source, routingKey, arguments); - } - - /// - /// Deletes an exchange. - /// - public static void ExchangeDelete(this IChannel channel, string exchange, bool ifUnused = false) - { - channel.ExchangeDelete(exchange, ifUnused); - } - - /// - /// Asynchronously deletes an exchange. - /// - public static ValueTask ExchangeDeleteAsync(this IChannel channel, string exchange, bool ifUnused = false) - { - return channel.ExchangeDeleteAsync(exchange, ifUnused); - } - - /// - /// Like ExchangeDelete but sets nowait to true. - /// - public static void ExchangeDeleteNoWait(this IChannel channel, string exchange, bool ifUnused = false) - { - channel.ExchangeDeleteNoWait(exchange, ifUnused); - } - - /// - /// Binds a queue. - /// - public static void QueueBind(this IChannel channel, string queue, string exchange, string routingKey, IDictionary arguments = null) - { - channel.QueueBind(queue, exchange, routingKey, arguments); - } - - /// - /// Asynchronously binds a queue. - /// - public static ValueTask QueueBindAsync(this IChannel channel, string queue, string exchange, string routingKey, IDictionary arguments = null) - { - return channel.QueueBindAsync(queue, exchange, routingKey, arguments); - } - - /// - /// Deletes a queue. - /// - public static uint QueueDelete(this IChannel channel, string queue, bool ifUnused = false, bool ifEmpty = false) - { - return channel.QueueDelete(queue, ifUnused, ifEmpty); + return channel.ExchangeDeclareAsync(exchange, type, durable, autoDelete, + arguments: arguments, passive: false, noWait: noWait); } /// /// Asynchronously deletes a queue. /// - public static ValueTask QueueDeleteAsync(this IChannel channel, string queue, bool ifUnused = false, bool ifEmpty = false) + public static Task QueueDeleteAsync(this IChannel channel, string queue, bool ifUnused = false, bool ifEmpty = false) { return channel.QueueDeleteAsync(queue, ifUnused, ifEmpty); } - /// - /// Like QueueDelete but sets nowait to true. - /// - public static void QueueDeleteNoWait(this IChannel channel, string queue, bool ifUnused = false, bool ifEmpty = false) - { - channel.QueueDeleteNoWait(queue, ifUnused, ifEmpty); - } - - /// - /// Unbinds a queue. - /// - public static void QueueUnbind(this IChannel channel, string queue, string exchange, string routingKey, IDictionary arguments = null) - { - channel.QueueUnbind(queue, exchange, routingKey, arguments); - } - /// /// Asynchronously unbinds a queue. /// - public static ValueTask QueueUnbindAsync(this IChannel channel, string queue, string exchange, string routingKey, IDictionary arguments = null) + public static Task QueueUnbindAsync(this IChannel channel, string queue, string exchange, string routingKey, IDictionary arguments = null) { return channel.QueueUnbindAsync(queue, exchange, routingKey, arguments); } - /// - /// Abort this session. - /// - /// - /// If the session is already closed (or closing), then this - /// method does nothing but wait for the in-progress close - /// operation to complete. This method will not return to the - /// caller until the shutdown is complete. - /// In comparison to normal method, will not throw - /// or or any other during closing channel. - /// - public static void Abort(this IChannel channel) - { - channel.Close(Constants.ReplySuccess, "Goodbye", true); - } - /// /// Asynchronously abort this session. /// @@ -346,44 +145,14 @@ public static void Abort(this IChannel channel) /// method does nothing but wait for the in-progress close /// operation to complete. This method will not return to the /// caller until the shutdown is complete. - /// In comparison to normal method, will not throw + /// In comparison to normal method, will not throw /// or or any other during closing channel. /// - public static ValueTask AbortAsync(this IChannel channel) + public static Task AbortAsync(this IChannel channel) { return channel.CloseAsync(Constants.ReplySuccess, "Goodbye", true); } - /// - /// Abort this session. - /// - /// - /// The method behaves in the same way as , with the only - /// difference that the channel is closed with the given channel close code and message. - /// - /// The close code (See under "Reply Codes" in the AMQP specification) - /// - /// - /// A message indicating the reason for closing the channel - /// - /// - public static void Abort(this IChannel channel, ushort replyCode, string replyText) - { - channel.Close(replyCode, replyText, true); - } - - /// Close this session. - /// - /// If the session is already closed (or closing), then this - /// method does nothing but wait for the in-progress close - /// operation to complete. This method will not return to the - /// caller until the shutdown is complete. - /// - public static void Close(this IChannel channel) - { - channel.Close(Constants.ReplySuccess, "Goodbye", false); - } - /// Asynchronously close this session. /// /// If the session is already closed (or closing), then this @@ -391,32 +160,11 @@ public static void Close(this IChannel channel) /// operation to complete. This method will not return to the /// caller until the shutdown is complete. /// - public static ValueTask CloseAsync(this IChannel channel) + public static Task CloseAsync(this IChannel channel) { return channel.CloseAsync(Constants.ReplySuccess, "Goodbye", false); } - /// - /// Close this channel. - /// - /// The channel. - /// The reply code. - /// The reply text. - /// - /// The method behaves in the same way as Close(), with the only - /// difference that the channel is closed with the given channel - /// close code and message. - /// - /// The close code (See under "Reply Codes" in the AMQP specification) - /// - /// A message indicating the reason for closing the channel - /// - /// - public static void Close(this IChannel channel, ushort replyCode, string replyText) - { - channel.Close(replyCode, replyText, false); - } - /// /// Asynchronously close this channel. /// @@ -433,7 +181,7 @@ public static void Close(this IChannel channel, ushort replyCode, string replyTe /// A message indicating the reason for closing the channel /// /// - public static ValueTask CloseAsync(this IChannel channel, ushort replyCode, string replyText) + public static Task CloseAsync(this IChannel channel, ushort replyCode, string replyText) { return channel.CloseAsync(replyCode, replyText, false); } diff --git a/projects/RabbitMQ.Client/client/api/IConnection.cs b/projects/RabbitMQ.Client/client/api/IConnection.cs index 11653032a1..d7fec6358c 100644 --- a/projects/RabbitMQ.Client/client/api/IConnection.cs +++ b/projects/RabbitMQ.Client/client/api/IConnection.cs @@ -214,17 +214,7 @@ public interface IConnection : INetworkConnection, IDisposable /// /// The new secret. /// The reason for the secret update. - void UpdateSecret(string newSecret, string reason); - - /// - /// Close this connection and all its channels - /// and wait with a timeout for all the in-progress close operations to complete. - /// - /// The close code (See under "Reply Codes" in the AMQP 0-9-1 specification). - /// A message indicating the reason for closing the connection. - /// Operation timeout. - /// Whether or not this close is an abort (ignores certain exceptions). - void Close(ushort reasonCode, string reasonText, TimeSpan timeout, bool abort); + Task UpdateSecretAsync(string newSecret, string reason); /// /// Asynchronously close this connection and all its channels @@ -234,17 +224,12 @@ public interface IConnection : INetworkConnection, IDisposable /// A message indicating the reason for closing the connection. /// Operation timeout. /// Whether or not this close is an abort (ignores certain exceptions). - ValueTask CloseAsync(ushort reasonCode, string reasonText, TimeSpan timeout, bool abort); - - /// - /// Create and return a fresh channel, session, and channel. - /// - IChannel CreateChannel(); + Task CloseAsync(ushort reasonCode, string reasonText, TimeSpan timeout, bool abort); /// /// Asynchronously create and return a fresh channel, session, and channel. /// // TODO cancellation token - ValueTask CreateChannelAsync(); + Task CreateChannelAsync(); } } diff --git a/projects/RabbitMQ.Client/client/api/IConnectionExtensions.cs b/projects/RabbitMQ.Client/client/api/IConnectionExtensions.cs index fecb606b78..6f879fae0e 100644 --- a/projects/RabbitMQ.Client/client/api/IConnectionExtensions.cs +++ b/projects/RabbitMQ.Client/client/api/IConnectionExtensions.cs @@ -7,22 +7,6 @@ namespace RabbitMQ.Client { public static class IConnectionExtensions { - /// - /// Close this connection and all its channels. - /// - /// - /// Note that all active channels and sessions will be - /// closed if this method is called. It will wait for the in-progress - /// close operation to complete. This method will not return to the caller - /// until the shutdown is complete. If the connection is already closed - /// (or closing), then this method will do nothing. - /// It can also throw when socket was closed unexpectedly. - /// - public static void Close(this IConnection connection) - { - connection.Close(Constants.ReplySuccess, "Goodbye", InternalConstants.DefaultConnectionCloseTimeout, false); - } - /// /// Asynchronously close this connection and all its channels. /// @@ -34,34 +18,16 @@ public static void Close(this IConnection connection) /// (or closing), then this method will do nothing. /// It can also throw when socket was closed unexpectedly. /// - public static ValueTask CloseAsync(this IConnection connection) + public static Task CloseAsync(this IConnection connection) { return connection.CloseAsync(Constants.ReplySuccess, "Goodbye", InternalConstants.DefaultConnectionCloseTimeout, false); } - /// - /// Close this connection and all its channels. - /// - /// - /// The method behaves in the same way as , with the only - /// difference that the connection is closed with the given connection close code and message. - /// - /// The close code (See under "Reply Codes" in the AMQP specification). - /// - /// - /// A message indicating the reason for closing the connection. - /// - /// - public static void Close(this IConnection connection, ushort reasonCode, string reasonText) - { - connection.Close(reasonCode, reasonText, InternalConstants.DefaultConnectionCloseTimeout, false); - } - /// /// Asynchronously close this connection and all its channels. /// /// - /// The method behaves in the same way as , with the only + /// The method behaves in the same way as , with the only /// difference that the connection is closed with the given connection close code and message. /// /// The close code (See under "Reply Codes" in the AMQP specification). @@ -70,31 +36,11 @@ public static void Close(this IConnection connection, ushort reasonCode, string /// A message indicating the reason for closing the connection. /// /// - public static ValueTask CloseAsync(this IConnection connection, ushort reasonCode, string reasonText) + public static Task CloseAsync(this IConnection connection, ushort reasonCode, string reasonText) { return connection.CloseAsync(reasonCode, reasonText, InternalConstants.DefaultConnectionCloseTimeout, false); } - /// - /// Close this connection and all its channels - /// and wait with a timeout for all the in-progress close operations to complete. - /// - /// - /// Note that all active channels and sessions will be - /// closed if this method is called. It will wait for the in-progress - /// close operation to complete with a timeout. If the connection is - /// already closed (or closing), then this method will do nothing. - /// It can also throw when socket was closed unexpectedly. - /// If timeout is reached and the close operations haven't finished, then socket is forced to close. - /// - /// To wait infinitely for the close operations to complete use . - /// - /// - public static void Close(this IConnection connection, TimeSpan timeout) - { - connection.Close(Constants.ReplySuccess, "Goodbye", timeout, false); - } - /// /// Asynchronously close this connection and all its channels /// and wait with a timeout for all the in-progress close operations to complete. @@ -110,39 +56,17 @@ public static void Close(this IConnection connection, TimeSpan timeout) /// To wait infinitely for the close operations to complete use . /// /// - public static ValueTask CloseAsync(this IConnection connection, TimeSpan timeout) + public static Task CloseAsync(this IConnection connection, TimeSpan timeout) { return connection.CloseAsync(Constants.ReplySuccess, "Goodbye", timeout, false); } - /// - /// Close this connection and all its channels - /// and wait with a timeout for all the in-progress close operations to complete. - /// - /// - /// The method behaves in the same way as , with the only - /// difference that the connection is closed with the given connection close code and message. - /// - /// The close code (See under "Reply Codes" in the AMQP 0-9-1 specification). - /// - /// - /// A message indicating the reason for closing the connection. - /// - /// - /// Operation timeout. - /// - /// - public static void Close(this IConnection connection, ushort reasonCode, string reasonText, TimeSpan timeout) - { - connection.Close(reasonCode, reasonText, timeout, false); - } - /// /// Asynchronously close this connection and all its channels /// and wait with a timeout for all the in-progress close operations to complete. /// /// - /// The method behaves in the same way as , with the only + /// The method behaves in the same way as , with the only /// difference that the connection is closed with the given connection close code and message. /// /// The close code (See under "Reply Codes" in the AMQP 0-9-1 specification). @@ -154,62 +78,30 @@ public static void Close(this IConnection connection, ushort reasonCode, string /// Operation timeout. /// /// - public static ValueTask CloseAsync(this IConnection connection, ushort reasonCode, string reasonText, TimeSpan timeout) + public static Task CloseAsync(this IConnection connection, ushort reasonCode, string reasonText, TimeSpan timeout) { return connection.CloseAsync(reasonCode, reasonText, timeout, false); } - /// - /// Abort this connection and all its channels. - /// - /// - /// Note that all active channels and sessions will be closed if this method is called. - /// In comparison to normal method, will not throw - /// during closing connection. - ///This method waits infinitely for the in-progress close operation to complete. - /// - public static void Abort(this IConnection connection) - { - connection.Close(Constants.ReplySuccess, "Connection close forced", InternalConstants.DefaultConnectionAbortTimeout, true); - } - /// /// Asynchronously abort this connection and all its channels. /// /// /// Note that all active channels and sessions will be closed if this method is called. - /// In comparison to normal method, will not throw + /// In comparison to normal method, will not throw /// during closing connection. ///This method waits infinitely for the in-progress close operation to complete. /// - public static ValueTask AbortAsync(this IConnection connection) + public static Task AbortAsync(this IConnection connection) { return connection.CloseAsync(Constants.ReplySuccess, "Connection close forced", InternalConstants.DefaultConnectionAbortTimeout, true); } - /// - /// Abort this connection and all its channels. - /// - /// - /// The method behaves in the same way as , with the only - /// difference that the connection is closed with the given connection close code and message. - /// - /// The close code (See under "Reply Codes" in the AMQP 0-9-1 specification) - /// - /// - /// A message indicating the reason for closing the connection - /// - /// - public static void Abort(this IConnection connection, ushort reasonCode, string reasonText) - { - connection.Close(reasonCode, reasonText, InternalConstants.DefaultConnectionAbortTimeout, true); - } - /// /// Asynchronously abort this connection and all its channels. /// /// - /// The method behaves in the same way as , with the only + /// The method behaves in the same way as , with the only /// difference that the connection is closed with the given connection close code and message. /// /// The close code (See under "Reply Codes" in the AMQP 0-9-1 specification) @@ -218,35 +110,17 @@ public static void Abort(this IConnection connection, ushort reasonCode, string /// A message indicating the reason for closing the connection /// /// - public static ValueTask AbortAsync(this IConnection connection, ushort reasonCode, string reasonText) + public static Task AbortAsync(this IConnection connection, ushort reasonCode, string reasonText) { return connection.CloseAsync(reasonCode, reasonText, InternalConstants.DefaultConnectionAbortTimeout, true); } - /// - /// Abort this connection and all its channels and wait with a - /// timeout for all the in-progress close operations to complete. - /// - /// - /// This method, behaves in a similar way as method with the - /// only difference that it explicitly specifies a timeout given - /// for all the in-progress close operations to complete. - /// If timeout is reached and the close operations haven't finished, then socket is forced to close. - /// - /// To wait infinitely for the close operations to complete use . - /// - /// - public static void Abort(this IConnection connection, TimeSpan timeout) - { - connection.Close(Constants.ReplySuccess, "Connection close forced", timeout, true); - } - /// /// Asynchronously abort this connection and all its channels and wait with a /// timeout for all the in-progress close operations to complete. /// /// - /// This method, behaves in a similar way as method with the + /// This method, behaves in a similar way as method with the /// only difference that it explicitly specifies a timeout given /// for all the in-progress close operations to complete. /// If timeout is reached and the close operations haven't finished, then socket is forced to close. @@ -254,36 +128,17 @@ public static void Abort(this IConnection connection, TimeSpan timeout) /// To wait infinitely for the close operations to complete use . /// /// - public static ValueTask AbortAsync(this IConnection connection, TimeSpan timeout) + public static Task AbortAsync(this IConnection connection, TimeSpan timeout) { return connection.CloseAsync(Constants.ReplySuccess, "Connection close forced", timeout, true); } - /// - /// Abort this connection and all its channels and wait with a - /// timeout for all the in-progress close operations to complete. - /// - /// - /// The method behaves in the same way as , with the only - /// difference that the connection is closed with the given connection close code and message. - /// - /// The close code (See under "Reply Codes" in the AMQP 0-9-1 specification). - /// - /// - /// A message indicating the reason for closing the connection. - /// - /// - public static void Abort(this IConnection connection, ushort reasonCode, string reasonText, TimeSpan timeout) - { - connection.Close(reasonCode, reasonText, timeout, true); - } - /// /// Asynchronously abort this connection and all its channels and wait with a /// timeout for all the in-progress close operations to complete. /// /// - /// The method behaves in the same way as , with the only + /// The method behaves in the same way as , with the only /// difference that the connection is closed with the given connection close code and message. /// /// The close code (See under "Reply Codes" in the AMQP 0-9-1 specification). @@ -292,7 +147,7 @@ public static void Abort(this IConnection connection, ushort reasonCode, string /// A message indicating the reason for closing the connection. /// /// - public static ValueTask AbortAsync(this IConnection connection, ushort reasonCode, string reasonText, TimeSpan timeout) + public static Task AbortAsync(this IConnection connection, ushort reasonCode, string reasonText, TimeSpan timeout) { return connection.CloseAsync(reasonCode, reasonText, timeout, true); } diff --git a/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs b/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs index a555b8b039..f80391d969 100644 --- a/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs +++ b/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs @@ -98,28 +98,11 @@ public interface IConnectionFactory /// IAuthMechanismFactory AuthMechanismFactory(IEnumerable mechanismNames); - /// - /// Create a connection to the specified endpoint. - /// - IConnection CreateConnection(); - /// /// Asynchronously create a connection to the specified endpoint. /// /// Cancellation token for this connection - ValueTask CreateConnectionAsync(CancellationToken cancellationToken = default); - - /// - /// Create a connection to the specified endpoint. - /// - /// - /// Application-specific connection name, will be displayed in the management UI - /// if RabbitMQ server supports it. This value doesn't have to be unique and cannot - /// be used as a connection identifier, e.g. in HTTP API requests. - /// This value is supposed to be human-readable. - /// - /// Open connection - IConnection CreateConnection(string clientProvidedName); + Task CreateConnectionAsync(CancellationToken cancellationToken = default); /// /// Asynchronously create a connection to the specified endpoint. @@ -132,14 +115,7 @@ public interface IConnectionFactory /// /// Cancellation token for this connection /// Open connection - ValueTask CreateConnectionAsync(string clientProvidedName, CancellationToken cancellationToken = default); - - /// - /// Connects to the first reachable hostname from the list. - /// - /// List of host names to use - /// Open connection - IConnection CreateConnection(IEnumerable hostnames); + Task CreateConnectionAsync(string clientProvidedName, CancellationToken cancellationToken = default); /// /// Asynchronously connects to the first reachable hostname from the list. @@ -147,20 +123,7 @@ public interface IConnectionFactory /// List of host names to use /// Cancellation token for this connection /// Open connection - ValueTask CreateConnectionAsync(IEnumerable hostnames, CancellationToken cancellationToken = default); - - /// - /// Connects to the first reachable hostname from the list. - /// - /// List of host names to use - /// - /// Application-specific connection name, will be displayed in the management UI - /// if RabbitMQ server supports it. This value doesn't have to be unique and cannot - /// be used as a connection identifier, e.g. in HTTP API requests. - /// This value is supposed to be human-readable. - /// - /// Open connection - IConnection CreateConnection(IEnumerable hostnames, string clientProvidedName); + Task CreateConnectionAsync(IEnumerable hostnames, CancellationToken cancellationToken = default); /// /// Asynchronously connects to the first reachable hostname from the list. @@ -174,23 +137,9 @@ public interface IConnectionFactory /// /// Cancellation token for this connection /// Open connection - ValueTask CreateConnectionAsync(IEnumerable hostnames, string clientProvidedName, + Task CreateConnectionAsync(IEnumerable hostnames, string clientProvidedName, CancellationToken cancellationToken = default); - /// - /// Create a connection using a list of endpoints. - /// The selection behaviour can be overridden by configuring the EndpointResolverFactory. - /// - /// - /// List of endpoints to use for the initial - /// connection and recovery. - /// - /// Open connection - /// - /// When no hostname was reachable. - /// - IConnection CreateConnection(IEnumerable endpoints); - /// /// Asynchronously create a connection using a list of endpoints. /// The selection behaviour can be overridden by configuring the EndpointResolverFactory. @@ -204,27 +153,7 @@ public interface IConnectionFactory /// /// When no hostname was reachable. /// - ValueTask CreateConnectionAsync(IEnumerable endpoints, CancellationToken cancellationToken = default); - - /// - /// Create a connection using a list of endpoints. - /// The selection behaviour can be overridden by configuring the EndpointResolverFactory. - /// - /// - /// List of endpoints to use for the initial - /// connection and recovery. - /// - /// - /// Application-specific connection name, will be displayed in the management UI - /// if RabbitMQ server supports it. This value doesn't have to be unique and cannot - /// be used as a connection identifier, e.g. in HTTP API requests. - /// This value is supposed to be human-readable. - /// - /// Open connection - /// - /// When no hostname was reachable. - /// - IConnection CreateConnection(IEnumerable endpoints, string clientProvidedName); + Task CreateConnectionAsync(IEnumerable endpoints, CancellationToken cancellationToken = default); /// /// Asynchronously create a connection using a list of endpoints. @@ -245,7 +174,7 @@ public interface IConnectionFactory /// /// When no hostname was reachable. /// - ValueTask CreateConnectionAsync(IEnumerable endpoints, string clientProvidedName, + Task CreateConnectionAsync(IEnumerable endpoints, string clientProvidedName, CancellationToken cancellationToken = default); /// diff --git a/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs b/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs index 08b474c2cb..2d648f1f87 100644 --- a/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs +++ b/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs @@ -33,14 +33,15 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Tracing; +using System.Threading.Tasks; namespace RabbitMQ.Client { public interface ICredentialsRefresher { - ICredentialsProvider Register(ICredentialsProvider provider, NotifyCredentialRefreshed callback); + ICredentialsProvider Register(ICredentialsProvider provider, NotifyCredentialRefreshedAsync callback); bool Unregister(ICredentialsProvider provider); - delegate void NotifyCredentialRefreshed(bool successfully); + delegate Task NotifyCredentialRefreshedAsync(bool successfully); } [EventSource(Name = "TimerBasedCredentialRefresher")] @@ -70,9 +71,9 @@ public class TimerBasedCredentialRefresherEventSource : EventSource public class TimerBasedCredentialRefresher : ICredentialsRefresher { - private readonly ConcurrentDictionary _registrations = new(); + private readonly ConcurrentDictionary _registrations = new ConcurrentDictionary(); - public ICredentialsProvider Register(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshed callback) + public ICredentialsProvider Register(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshedAsync callback) { if (!provider.ValidUntil.HasValue || provider.ValidUntil.Value.Equals(TimeSpan.Zero)) { @@ -112,7 +113,7 @@ public bool Unregister(ICredentialsProvider provider) } } - private System.Timers.Timer scheduleTimer(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshed callback) + private System.Timers.Timer scheduleTimer(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshedAsync callback) { System.Timers.Timer timer = new System.Timers.Timer(); timer.Interval = provider.ValidUntil.Value.TotalMilliseconds * (1.0 - (1 / 3.0)); @@ -142,7 +143,7 @@ private System.Timers.Timer scheduleTimer(ICredentialsProvider provider, ICreden class NoOpCredentialsRefresher : ICredentialsRefresher { - public ICredentialsProvider Register(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshed callback) + public ICredentialsProvider Register(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshedAsync callback) { return provider; } diff --git a/projects/RabbitMQ.Client/client/api/TopologyRecoveryExceptionHandler.cs b/projects/RabbitMQ.Client/client/api/TopologyRecoveryExceptionHandler.cs index a40097f383..4efc3c54e8 100644 --- a/projects/RabbitMQ.Client/client/api/TopologyRecoveryExceptionHandler.cs +++ b/projects/RabbitMQ.Client/client/api/TopologyRecoveryExceptionHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; namespace RabbitMQ.Client { @@ -16,10 +17,10 @@ public class TopologyRecoveryExceptionHandler private Func _queueRecoveryExceptionCondition; private Func _bindingRecoveryExceptionCondition; private Func _consumerRecoveryExceptionCondition; - private Action _exchangeRecoveryExceptionHandler; - private Action _queueRecoveryExceptionHandler; - private Action _bindingRecoveryExceptionHandler; - private Action _consumerRecoveryExceptionHandler; + private Func _exchangeRecoveryExceptionHandlerAsync; + private Func _queueRecoveryExceptionHandlerAsync; + private Func _bindingRecoveryExceptionHandlerAsync; + private Func _consumerRecoveryExceptionHandlerAsync; /// /// Decides which exchange recovery exceptions the custom exception handler is applied to. @@ -92,64 +93,72 @@ public class TopologyRecoveryExceptionHandler /// /// Retries, or otherwise handles, an exception thrown when attempting to recover an exchange. /// - public Action ExchangeRecoveryExceptionHandler + public Func ExchangeRecoveryExceptionHandlerAsync { - get => _exchangeRecoveryExceptionHandler; + get => _exchangeRecoveryExceptionHandlerAsync; set { - if (_exchangeRecoveryExceptionHandler != null) - throw new InvalidOperationException($"Cannot modify {nameof(ExchangeRecoveryExceptionHandler)} after it has been initialized."); + if (_exchangeRecoveryExceptionHandlerAsync != null) + { + throw new InvalidOperationException($"Cannot modify {nameof(ExchangeRecoveryExceptionHandlerAsync)} after it has been initialized."); + } - _exchangeRecoveryExceptionHandler = value ?? throw new ArgumentNullException(nameof(ExchangeRecoveryExceptionHandler)); + _exchangeRecoveryExceptionHandlerAsync = value ?? throw new ArgumentNullException(nameof(ExchangeRecoveryExceptionHandlerAsync)); } } /// /// Retries, or otherwise handles, an exception thrown when attempting to recover a queue. /// - public Action QueueRecoveryExceptionHandler + public Func QueueRecoveryExceptionHandlerAsync { - get => _queueRecoveryExceptionHandler; + get => _queueRecoveryExceptionHandlerAsync; set { - if (_queueRecoveryExceptionHandler != null) - throw new InvalidOperationException($"Cannot modify {nameof(QueueRecoveryExceptionHandler)} after it has been initialized."); + if (_queueRecoveryExceptionHandlerAsync != null) + { + throw new InvalidOperationException($"Cannot modify {nameof(QueueRecoveryExceptionHandlerAsync)} after it has been initialized."); + } - _queueRecoveryExceptionHandler = value ?? throw new ArgumentNullException(nameof(QueueRecoveryExceptionHandler)); + _queueRecoveryExceptionHandlerAsync = value ?? throw new ArgumentNullException(nameof(QueueRecoveryExceptionHandlerAsync)); } } /// /// Retries, or otherwise handles, an exception thrown when attempting to recover a binding. /// - public Action BindingRecoveryExceptionHandler + public Func BindingRecoveryExceptionHandlerAsync { - get => _bindingRecoveryExceptionHandler; + get => _bindingRecoveryExceptionHandlerAsync; set { - if (_bindingRecoveryExceptionHandler != null) - throw new InvalidOperationException($"Cannot modify {nameof(BindingRecoveryExceptionHandler)} after it has been initialized."); + if (_bindingRecoveryExceptionHandlerAsync != null) + { + throw new InvalidOperationException($"Cannot modify {nameof(BindingRecoveryExceptionHandlerAsync)} after it has been initialized."); + } - _bindingRecoveryExceptionHandler = value ?? throw new ArgumentNullException(nameof(BindingRecoveryExceptionHandler)); + _bindingRecoveryExceptionHandlerAsync = value ?? throw new ArgumentNullException(nameof(BindingRecoveryExceptionHandlerAsync)); } } /// /// Retries, or otherwise handles, an exception thrown when attempting to recover a consumer. /// - public Action ConsumerRecoveryExceptionHandler + public Func ConsumerRecoveryExceptionHandlerAsync { - get => _consumerRecoveryExceptionHandler; + get => _consumerRecoveryExceptionHandlerAsync; set { - if (_consumerRecoveryExceptionHandler != null) - throw new InvalidOperationException($"Cannot modify {nameof(ConsumerRecoveryExceptionHandler)} after it has been initialized."); + if (_consumerRecoveryExceptionHandlerAsync != null) + { + throw new InvalidOperationException($"Cannot modify {nameof(ConsumerRecoveryExceptionHandlerAsync)} after it has been initialized."); + } - _consumerRecoveryExceptionHandler = value ?? throw new ArgumentNullException(nameof(ConsumerRecoveryExceptionHandler)); + _consumerRecoveryExceptionHandlerAsync = value ?? throw new ArgumentNullException(nameof(ConsumerRecoveryExceptionHandlerAsync)); } } } diff --git a/projects/RabbitMQ.Client/client/events/EventingBasicConsumer.cs b/projects/RabbitMQ.Client/client/events/EventingBasicConsumer.cs index 4a6bad730b..d69d72e23a 100644 --- a/projects/RabbitMQ.Client/client/events/EventingBasicConsumer.cs +++ b/projects/RabbitMQ.Client/client/events/EventingBasicConsumer.cs @@ -30,6 +30,7 @@ //--------------------------------------------------------------------------- using System; +using System.Threading.Tasks; namespace RabbitMQ.Client.Events { @@ -84,10 +85,10 @@ public override void HandleBasicConsumeOk(string consumerTag) /// Accessing the body at a later point is unsafe as its memory can /// be already released. /// - public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - in ReadOnlyBasicProperties properties, ReadOnlyMemory body) + public override async Task HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, + ReadOnlyBasicProperties properties, ReadOnlyMemory body) { - base.HandleBasicDeliver(consumerTag, deliveryTag, redelivered, exchange, routingKey, properties, body); + await base.HandleBasicDeliverAsync(consumerTag, deliveryTag, redelivered, exchange, routingKey, properties, body); Received?.Invoke( this, new BasicDeliverEventArgs(consumerTag, deliveryTag, redelivered, exchange, routingKey, properties, body)); diff --git a/projects/RabbitMQ.Client/client/framing/Channel.cs b/projects/RabbitMQ.Client/client/framing/Channel.cs index 9b7246799e..129c4ee809 100644 --- a/projects/RabbitMQ.Client/client/framing/Channel.cs +++ b/projects/RabbitMQ.Client/client/framing/Channel.cs @@ -29,7 +29,6 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- -using System.Collections.Generic; using System.Threading.Tasks; using RabbitMQ.Client.client.framing; using RabbitMQ.Client.Impl; @@ -47,21 +46,6 @@ public override void ConnectionTuneOk(ushort channelMax, uint frameMax, ushort h ChannelSend(new ConnectionTuneOk(channelMax, frameMax, heartbeat)); } - public override void _Private_BasicCancel(string consumerTag, bool nowait) - { - ChannelSend(new BasicCancel(consumerTag, nowait)); - } - - public override void _Private_BasicConsume(string queue, string consumerTag, bool noLocal, bool autoAck, bool exclusive, bool nowait, IDictionary arguments) - { - ChannelSend(new BasicConsume(queue, consumerTag, noLocal, autoAck, exclusive, nowait, arguments)); - } - - public override void _Private_BasicGet(string queue, bool autoAck) - { - ChannelSend(new BasicGet(queue, autoAck)); - } - public override void _Private_ChannelClose(ushort replyCode, string replyText, ushort classId, ushort methodId) { ChannelSend(new ChannelClose(replyCode, replyText, classId, methodId)); @@ -77,189 +61,27 @@ public override void _Private_ChannelFlowOk(bool active) ChannelSend(new ChannelFlowOk(active)); } - public override void _Private_ChannelOpen() - { - ChannelRpc(new ChannelOpen(), ProtocolCommandId.ChannelOpenOk); - } - - public override void _Private_ConfirmSelect(bool nowait) - { - var method = new ConfirmSelect(nowait); - if (nowait) - { - ChannelSend(method); - } - else - { - ChannelRpc(method, ProtocolCommandId.ConfirmSelectOk); - } - } - public override void _Private_ConnectionCloseOk() { ChannelSend(new ConnectionCloseOk()); } - public override void _Private_UpdateSecret(byte[] newSecret, string reason) - { - ChannelRpc(new ConnectionUpdateSecret(newSecret, reason), ProtocolCommandId.ConnectionUpdateSecretOk); - } - - public override void _Private_ExchangeBind(string destination, string source, string routingKey, bool nowait, IDictionary arguments) - { - var method = new ExchangeBind(destination, source, routingKey, nowait, arguments); - if (nowait) - { - ChannelSend(method); - } - else - { - ChannelRpc(method, ProtocolCommandId.ExchangeBindOk); - } - } - - public override void _Private_ExchangeDeclare(string exchange, string type, bool passive, bool durable, bool autoDelete, bool @internal, bool nowait, IDictionary arguments) - { - var method = new ExchangeDeclare(exchange, type, passive, durable, autoDelete, @internal, nowait, arguments); - if (nowait) - { - ChannelSend(method); - } - else - { - ChannelRpc(method, ProtocolCommandId.ExchangeDeclareOk); - } - } - - public override void _Private_ExchangeDelete(string exchange, bool ifUnused, bool nowait) - { - var method = new ExchangeDelete(exchange, ifUnused, nowait); - if (nowait) - { - ChannelSend(method); - } - else - { - ChannelRpc(method, ProtocolCommandId.ExchangeDeleteOk); - } - } - - public override void _Private_ExchangeUnbind(string destination, string source, string routingKey, bool nowait, IDictionary arguments) - { - var method = new ExchangeUnbind(destination, source, routingKey, nowait, arguments); - if (nowait) - { - ChannelSend(method); - } - else - { - ChannelRpc(method, ProtocolCommandId.ExchangeUnbindOk); - } - } - - public override void _Private_QueueBind(string queue, string exchange, string routingKey, bool nowait, IDictionary arguments) - { - var method = new QueueBind(queue, exchange, routingKey, nowait, arguments); - if (nowait) - { - ChannelSend(method); - } - else - { - ChannelRpc(method, ProtocolCommandId.QueueBindOk); - } - } - - public override void _Private_QueueDeclare(string queue, bool passive, bool durable, bool exclusive, bool autoDelete, bool nowait, IDictionary arguments) - { - /* - * Note: - * Even though nowait is a parameter, ChannelSend must be used - */ - var method = new QueueDeclare(queue, passive, durable, exclusive, autoDelete, nowait, arguments); - ChannelSend(method); - } - - public override uint _Private_QueueDelete(string queue, bool ifUnused, bool ifEmpty, bool nowait) - { - var method = new QueueDelete(queue, ifUnused, ifEmpty, nowait); - if (nowait) - { - ChannelSend(method); - return 0xFFFFFFFF; - } - - return ChannelRpc(method, ProtocolCommandId.QueueDeleteOk, memory => new QueueDeleteOk(memory.Span)._messageCount); - } - - public override uint _Private_QueuePurge(string queue, bool nowait) - { - var method = new QueuePurge(queue, nowait); - if (nowait) - { - ChannelSend(method); - return 0xFFFFFFFF; - } - - return ChannelRpc(method, ProtocolCommandId.QueuePurgeOk, memory => new QueuePurgeOk(memory.Span)._messageCount); - } - - public override void BasicAck(ulong deliveryTag, bool multiple) - { - ChannelSend(new BasicAck(deliveryTag, multiple)); - } - public override ValueTask BasicAckAsync(ulong deliveryTag, bool multiple) { var method = new BasicAck(deliveryTag, multiple); return ModelSendAsync(method); } - public override void BasicNack(ulong deliveryTag, bool multiple, bool requeue) - { - ChannelSend(new BasicNack(deliveryTag, multiple, requeue)); - } - public override ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue) { var method = new BasicNack(deliveryTag, multiple, requeue); return ModelSendAsync(method); } - public override void BasicQos(uint prefetchSize, ushort prefetchCount, bool global) - { - ChannelRpc(new BasicQos(prefetchSize, prefetchCount, global), ProtocolCommandId.BasicQosOk); - } - - public override void BasicReject(ulong deliveryTag, bool requeue) - { - ChannelSend(new BasicReject(deliveryTag, requeue)); - } - - public override ValueTask BasicRejectAsync(ulong deliveryTag, bool requeue) + public override Task BasicRejectAsync(ulong deliveryTag, bool requeue) { var method = new BasicReject(deliveryTag, requeue); - return ModelSendAsync(method); - } - - public override void QueueUnbind(string queue, string exchange, string routingKey, IDictionary arguments) - { - ChannelRpc(new QueueUnbind(queue, exchange, routingKey, arguments), ProtocolCommandId.QueueUnbindOk); - } - - public override void TxCommit() - { - ChannelRpc(new TxCommit(), ProtocolCommandId.TxCommitOk); - } - - public override void TxRollback() - { - ChannelRpc(new TxRollback(), ProtocolCommandId.TxRollbackOk); - } - - public override void TxSelect() - { - ChannelRpc(new TxSelect(), ProtocolCommandId.TxSelectOk); + return ModelSendAsync(method).AsTask(); } protected override bool DispatchAsynchronous(in IncomingCommand cmd) diff --git a/projects/RabbitMQ.Client/client/impl/AsyncRpcContinuations.cs b/projects/RabbitMQ.Client/client/impl/AsyncRpcContinuations.cs index c4c4955fb2..f673e00091 100644 --- a/projects/RabbitMQ.Client/client/impl/AsyncRpcContinuations.cs +++ b/projects/RabbitMQ.Client/client/impl/AsyncRpcContinuations.cs @@ -126,7 +126,7 @@ public override void HandleCommand(in IncomingCommand cmd) // What to do if setting a result fails? _tcs.TrySetResult(new ConnectionSecureOrTune { - m_tuneDetails = new() { m_channelMax = tune._channelMax, m_frameMax = tune._frameMax, m_heartbeatInSeconds = tune._heartbeat } + m_tuneDetails = new ConnectionTuneDetails { m_channelMax = tune._channelMax, m_frameMax = tune._frameMax, m_heartbeatInSeconds = tune._heartbeat } }); } else diff --git a/projects/RabbitMQ.Client/client/impl/AutorecoveringChannel.cs b/projects/RabbitMQ.Client/client/impl/AutorecoveringChannel.cs index c2526f9954..3f6cdba60f 100644 --- a/projects/RabbitMQ.Client/client/impl/AutorecoveringChannel.cs +++ b/projects/RabbitMQ.Client/client/impl/AutorecoveringChannel.cs @@ -45,7 +45,7 @@ internal sealed class AutorecoveringChannel : IChannel, IRecoverable private AutorecoveringConnection _connection; private RecoveryAwareChannel _innerChannel; private bool _disposed; - private readonly List _recordedConsumerTags = new(); + private readonly List _recordedConsumerTags = new List(); private ushort _prefetchCountConsumer; private ushort _prefetchCountGlobal; @@ -145,7 +145,7 @@ public IBasicConsumer DefaultConsumer public string CurrentQueue => InnerChannel.CurrentQueue; - internal async ValueTask AutomaticallyRecoverAsync(AutorecoveringConnection conn, bool recoverConsumers, + internal async Task AutomaticallyRecoverAsync(AutorecoveringConnection conn, bool recoverConsumers, bool recordedEntitiesSemaphoreHeld = false) { if (false == recordedEntitiesSemaphoreHeld) @@ -215,14 +215,14 @@ public void Close(ushort replyCode, string replyText, bool abort) } } - public ValueTask CloseAsync(ushort replyCode, string replyText, bool abort) + public Task CloseAsync(ushort replyCode, string replyText, bool abort) { ThrowIfDisposed(); var args = new ShutdownEventArgs(ShutdownInitiator.Library, replyCode, replyText); return CloseAsync(args, abort); } - public async ValueTask CloseAsync(ShutdownEventArgs args, bool abort) + public async Task CloseAsync(ShutdownEventArgs args, bool abort) { ThrowIfDisposed(); try @@ -248,7 +248,12 @@ public void Dispose() return; } - this.Abort(); + // TODO rabbitmq-dotnet-client-1472 + // this.Abort(); + if (IsOpen) + { + throw new InvalidOperationException("AutorecoveringChannel must be closed before calling Dispose!"); + } _recordedConsumerTags.Clear(); _connection = null; @@ -256,45 +261,17 @@ public void Dispose() _disposed = true; } - public void BasicAck(ulong deliveryTag, bool multiple) - => InnerChannel.BasicAck(deliveryTag, multiple); - public ValueTask BasicAckAsync(ulong deliveryTag, bool multiple) => InnerChannel.BasicAckAsync(deliveryTag, multiple); - public void BasicCancel(string consumerTag) - { - ThrowIfDisposed(); - _connection.DeleteRecordedConsumer(consumerTag, recordedEntitiesSemaphoreHeld: false); - _innerChannel.BasicCancel(consumerTag); - } - - public ValueTask BasicCancelAsync(string consumerTag) - { - ThrowIfDisposed(); - _connection.DeleteRecordedConsumer(consumerTag, recordedEntitiesSemaphoreHeld: false); - return _innerChannel.BasicCancelAsync(consumerTag); - } - - public void BasicCancelNoWait(string consumerTag) + public Task BasicCancelAsync(string consumerTag, bool noWait) { ThrowIfDisposed(); _connection.DeleteRecordedConsumer(consumerTag, recordedEntitiesSemaphoreHeld: false); - _innerChannel.BasicCancelNoWait(consumerTag); + return _innerChannel.BasicCancelAsync(consumerTag, noWait); } - public string BasicConsume(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, - IDictionary arguments, IBasicConsumer consumer) - { - string resultConsumerTag = InnerChannel.BasicConsume(queue, autoAck, consumerTag, noLocal, exclusive, arguments, consumer); - var rc = new RecordedConsumer(channel: this, consumer: consumer, consumerTag: resultConsumerTag, - queue: queue, autoAck: autoAck, exclusive: exclusive, arguments: arguments); - _connection.RecordConsumer(rc, recordedEntitiesSemaphoreHeld: false); - _recordedConsumerTags.Add(resultConsumerTag); - return resultConsumerTag; - } - - public async ValueTask BasicConsumeAsync(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, + public async Task BasicConsumeAsync(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, IDictionary arguments, IBasicConsumer consumer) { string resultConsumerTag = await InnerChannel.BasicConsumeAsync(queue, autoAck, consumerTag, noLocal, exclusive, arguments, consumer) @@ -307,26 +284,12 @@ await _connection.RecordConsumerAsync(rc, recordedEntitiesSemaphoreHeld: false) return resultConsumerTag; } - public BasicGetResult BasicGet(string queue, bool autoAck) - => InnerChannel.BasicGet(queue, autoAck); - public ValueTask BasicGetAsync(string queue, bool autoAck) => InnerChannel.BasicGetAsync(queue, autoAck); - public void BasicNack(ulong deliveryTag, bool multiple, bool requeue) - => InnerChannel.BasicNack(deliveryTag, multiple, requeue); - public ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue) => InnerChannel.BasicNackAsync(deliveryTag, multiple, requeue); - public void BasicPublish(string exchange, string routingKey, in TProperties basicProperties, ReadOnlyMemory body, bool mandatory) - where TProperties : IReadOnlyBasicProperties, IAmqpHeader - => InnerChannel.BasicPublish(exchange, routingKey, in basicProperties, body, mandatory); - - public void BasicPublish(CachedString exchange, CachedString routingKey, in TProperties basicProperties, ReadOnlyMemory body, bool mandatory) - where TProperties : IReadOnlyBasicProperties, IAmqpHeader - => InnerChannel.BasicPublish(exchange, routingKey, in basicProperties, body, mandatory); - public ValueTask BasicPublishAsync(string exchange, string routingKey, in TProperties basicProperties, ReadOnlyMemory body, bool mandatory) where TProperties : IReadOnlyBasicProperties, IAmqpHeader => InnerChannel.BasicPublishAsync(exchange, routingKey, in basicProperties, body, mandatory); @@ -335,23 +298,7 @@ public ValueTask BasicPublishAsync(CachedString exchange, CachedStr where TProperties : IReadOnlyBasicProperties, IAmqpHeader => InnerChannel.BasicPublishAsync(exchange, routingKey, in basicProperties, body, mandatory); - public void BasicQos(uint prefetchSize, ushort prefetchCount, bool global) - { - ThrowIfDisposed(); - - if (global) - { - _prefetchCountGlobal = prefetchCount; - } - else - { - _prefetchCountConsumer = prefetchCount; - } - - _innerChannel.BasicQos(prefetchSize, prefetchCount, global); - } - - public ValueTask BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global) + public Task BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global) { ThrowIfDisposed(); @@ -367,55 +314,33 @@ public ValueTask BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool glo return _innerChannel.BasicQosAsync(prefetchSize, prefetchCount, global); } - public void BasicReject(ulong deliveryTag, bool requeue) - => InnerChannel.BasicReject(deliveryTag, requeue); - - public ValueTask BasicRejectAsync(ulong deliveryTag, bool requeue) + public Task BasicRejectAsync(ulong deliveryTag, bool requeue) => InnerChannel.BasicRejectAsync(deliveryTag, requeue); - public void ConfirmSelect() - { - InnerChannel.ConfirmSelect(); - _usesPublisherConfirms = true; - } - - public async ValueTask ConfirmSelectAsync() + public async Task ConfirmSelectAsync() { await InnerChannel.ConfirmSelectAsync() .ConfigureAwait(false); _usesPublisherConfirms = true; } - public void ExchangeBind(string destination, string source, string routingKey, IDictionary arguments) - { - ThrowIfDisposed(); - var recordedBinding = new RecordedBinding(false, destination, source, routingKey, arguments); - _connection.RecordBinding(recordedBinding, recordedEntitiesSemaphoreHeld: false); - _innerChannel.ExchangeBind(destination, source, routingKey, arguments); - } - - public async ValueTask ExchangeBindAsync(string destination, string source, string routingKey, IDictionary arguments) + public async Task ExchangeBindAsync(string destination, string source, string routingKey, + IDictionary arguments, bool noWait) { - await InnerChannel.ExchangeBindAsync(destination, source, routingKey, arguments) + await InnerChannel.ExchangeBindAsync(destination, source, routingKey, arguments, noWait) .ConfigureAwait(false); var recordedBinding = new RecordedBinding(false, destination, source, routingKey, arguments); await _connection.RecordBindingAsync(recordedBinding, recordedEntitiesSemaphoreHeld: false) .ConfigureAwait(false); } - public void ExchangeBindNoWait(string destination, string source, string routingKey, IDictionary arguments) - => InnerChannel.ExchangeBindNoWait(destination, source, routingKey, arguments); + public Task ExchangeDeclarePassiveAsync(string exchange) + => InnerChannel.ExchangeDeclarePassiveAsync(exchange); - public void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments) + public async Task ExchangeDeclareAsync(string exchange, string type, bool durable, bool autoDelete, + IDictionary arguments, bool passive, bool noWait) { - InnerChannel.ExchangeDeclare(exchange, type, durable, autoDelete, arguments); - var recordedExchange = new RecordedExchange(exchange, type, durable, autoDelete, arguments); - _connection.RecordExchange(recordedExchange, recordedEntitiesSemaphoreHeld: false); - } - - public async ValueTask ExchangeDeclareAsync(string exchange, string type, bool passive, bool durable, bool autoDelete, IDictionary arguments) - { - await InnerChannel.ExchangeDeclareAsync(exchange, type, passive, durable, autoDelete, arguments) + await InnerChannel.ExchangeDeclareAsync(exchange, type, durable, autoDelete, arguments, passive, noWait) .ConfigureAwait(false); if (false == passive) { @@ -425,89 +350,48 @@ await _connection.RecordExchangeAsync(recordedExchange, recordedEntitiesSemaphor } } - public void ExchangeDeclareNoWait(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments) - { - InnerChannel.ExchangeDeclareNoWait(exchange, type, durable, autoDelete, arguments); - var recordedExchange = new RecordedExchange(exchange, type, durable, autoDelete, arguments); - _connection.RecordExchange(recordedExchange, recordedEntitiesSemaphoreHeld: false); - } - - public void ExchangeDeclarePassive(string exchange) - => InnerChannel.ExchangeDeclarePassive(exchange); - - public void ExchangeDelete(string exchange, bool ifUnused) + public async Task ExchangeDeleteAsync(string exchange, bool ifUnused, bool noWait) { - InnerChannel.ExchangeDelete(exchange, ifUnused); - _connection.DeleteRecordedExchange(exchange, recordedEntitiesSemaphoreHeld: false); - } - - public async ValueTask ExchangeDeleteAsync(string exchange, bool ifUnused) - { - await InnerChannel.ExchangeDeleteAsync(exchange, ifUnused) + await InnerChannel.ExchangeDeleteAsync(exchange, ifUnused, noWait) .ConfigureAwait(false); await _connection.DeleteRecordedExchangeAsync(exchange, recordedEntitiesSemaphoreHeld: false) .ConfigureAwait(false); } - public void ExchangeDeleteNoWait(string exchange, bool ifUnused) - { - InnerChannel.ExchangeDeleteNoWait(exchange, ifUnused); - _connection.DeleteRecordedExchange(exchange, recordedEntitiesSemaphoreHeld: false); - } - - public void ExchangeUnbind(string destination, string source, string routingKey, IDictionary arguments) - { - ThrowIfDisposed(); - var recordedBinding = new RecordedBinding(false, destination, source, routingKey, arguments); - _connection.DeleteRecordedBinding(recordedBinding, recordedEntitiesSemaphoreHeld: false); - _innerChannel.ExchangeUnbind(destination, source, routingKey, arguments); - _connection.DeleteAutoDeleteExchange(source, recordedEntitiesSemaphoreHeld: false); - } - - public async ValueTask ExchangeUnbindAsync(string destination, string source, string routingKey, IDictionary arguments) + public async Task ExchangeUnbindAsync(string destination, string source, string routingKey, + IDictionary arguments, bool noWait) { ThrowIfDisposed(); var recordedBinding = new RecordedBinding(false, destination, source, routingKey, arguments); await _connection.DeleteRecordedBindingAsync(recordedBinding, recordedEntitiesSemaphoreHeld: false) .ConfigureAwait(false); - await InnerChannel.ExchangeUnbindAsync(destination, source, routingKey, arguments) + await InnerChannel.ExchangeUnbindAsync(destination, source, routingKey, arguments, noWait) .ConfigureAwait(false); await _connection.DeleteAutoDeleteExchangeAsync(source, recordedEntitiesSemaphoreHeld: false) .ConfigureAwait(false); } - public void ExchangeUnbindNoWait(string destination, string source, string routingKey, IDictionary arguments) - => InnerChannel.ExchangeUnbind(destination, source, routingKey, arguments); - - public void QueueBind(string queue, string exchange, string routingKey, IDictionary arguments) + public async Task QueueBindAsync(string queue, string exchange, string routingKey, + IDictionary arguments, bool noWait) { - ThrowIfDisposed(); + await InnerChannel.QueueBindAsync(queue, exchange, routingKey, arguments, noWait) + .ConfigureAwait(false); var recordedBinding = new RecordedBinding(true, queue, exchange, routingKey, arguments); - _connection.RecordBinding(recordedBinding, recordedEntitiesSemaphoreHeld: false); - _innerChannel.QueueBind(queue, exchange, routingKey, arguments); - } - - public void QueueBindNoWait(string queue, string exchange, string routingKey, IDictionary arguments) - => InnerChannel.QueueBind(queue, exchange, routingKey, arguments); - - public QueueDeclareOk QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) - { - QueueDeclareOk result = InnerChannel.QueueDeclare(queue, durable, exclusive, autoDelete, arguments); - var recordedQueue = new RecordedQueue(result.QueueName, queue.Length == 0, durable, exclusive, autoDelete, arguments); - _connection.RecordQueue(recordedQueue, recordedEntitiesSemaphoreHeld: false); - return result; + await _connection.RecordBindingAsync(recordedBinding, recordedEntitiesSemaphoreHeld: false) + .ConfigureAwait(false); } - public void QueueDeclareNoWait(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) + public Task QueueDeclarePassiveAsync(string queue) { - InnerChannel.QueueDeclareNoWait(queue, durable, exclusive, autoDelete, arguments); - var recordedQueue = new RecordedQueue(queue, queue.Length == 0, durable, exclusive, autoDelete, arguments); - _connection.RecordQueue(recordedQueue, recordedEntitiesSemaphoreHeld: false); + return QueueDeclareAsync(queue: queue, passive: true, + durable: false, exclusive: false, autoDelete: false, + arguments: null, noWait: false); } - public async ValueTask QueueDeclareAsync(string queue, bool passive, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) + public async Task QueueDeclareAsync(string queue, bool durable, bool exclusive, bool autoDelete, + IDictionary arguments, bool passive, bool noWait) { - QueueDeclareOk result = await InnerChannel.QueueDeclareAsync(queue, passive, durable, exclusive, autoDelete, arguments) + QueueDeclareOk result = await InnerChannel.QueueDeclareAsync(queue, durable, exclusive, autoDelete, arguments, passive, noWait) .ConfigureAwait(false); if (false == passive) { @@ -518,55 +402,24 @@ await _connection.RecordQueueAsync(recordedQueue, recordedEntitiesSemaphoreHeld: return result; } - public ValueTask QueueBindAsync(string queue, string exchange, string routingKey, IDictionary arguments) - => InnerChannel.QueueBindAsync(queue, exchange, routingKey, arguments); - - public QueueDeclareOk QueueDeclarePassive(string queue) - => InnerChannel.QueueDeclarePassive(queue); + public Task MessageCountAsync(string queue) + => InnerChannel.MessageCountAsync(queue); - public uint MessageCount(string queue) - => InnerChannel.MessageCount(queue); + public Task ConsumerCountAsync(string queue) + => InnerChannel.ConsumerCountAsync(queue); - public uint ConsumerCount(string queue) - => InnerChannel.ConsumerCount(queue); - - public uint QueueDelete(string queue, bool ifUnused, bool ifEmpty) + public async Task QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty, bool noWait) { - uint result = InnerChannel.QueueDelete(queue, ifUnused, ifEmpty); - _connection.DeleteRecordedQueue(queue, recordedEntitiesSemaphoreHeld: false); - return result; - } - - public async ValueTask QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty) - { - uint result = await InnerChannel.QueueDeleteAsync(queue, ifUnused, ifEmpty); + uint result = await InnerChannel.QueueDeleteAsync(queue, ifUnused, ifEmpty, noWait); await _connection.DeleteRecordedQueueAsync(queue, recordedEntitiesSemaphoreHeld: false) .ConfigureAwait(false); return result; } - public void QueueDeleteNoWait(string queue, bool ifUnused, bool ifEmpty) - { - InnerChannel.QueueDeleteNoWait(queue, ifUnused, ifEmpty); - _connection.DeleteRecordedQueue(queue, recordedEntitiesSemaphoreHeld: false); - } - - public uint QueuePurge(string queue) - => InnerChannel.QueuePurge(queue); - - public ValueTask QueuePurgeAsync(string queue) + public Task QueuePurgeAsync(string queue) => InnerChannel.QueuePurgeAsync(queue); - public void QueueUnbind(string queue, string exchange, string routingKey, IDictionary arguments) - { - ThrowIfDisposed(); - var recordedBinding = new RecordedBinding(true, queue, exchange, routingKey, arguments); - _connection.DeleteRecordedBinding(recordedBinding, recordedEntitiesSemaphoreHeld: false); - _innerChannel.QueueUnbind(queue, exchange, routingKey, arguments); - _connection.DeleteAutoDeleteExchange(exchange, recordedEntitiesSemaphoreHeld: false); - } - - public async ValueTask QueueUnbindAsync(string queue, string exchange, string routingKey, IDictionary arguments) + public async Task QueueUnbindAsync(string queue, string exchange, string routingKey, IDictionary arguments) { ThrowIfDisposed(); var recordedBinding = new RecordedBinding(true, queue, exchange, routingKey, arguments); @@ -578,39 +431,22 @@ await _connection.DeleteAutoDeleteExchangeAsync(exchange, recordedEntitiesSemaph .ConfigureAwait(false); } - public void TxCommit() - => InnerChannel.TxCommit(); - - public ValueTask TxCommitAsync() + public Task TxCommitAsync() => InnerChannel.TxCommitAsync(); - public void TxRollback() - => InnerChannel.TxRollback(); - public ValueTask TxRollbackAsync() + public Task TxRollbackAsync() => InnerChannel.TxRollbackAsync(); - public void TxSelect() - { - InnerChannel.TxSelect(); - _usesTransactions = true; - } - - public ValueTask TxSelectAsync() + public Task TxSelectAsync() { _usesTransactions = true; return InnerChannel.TxSelectAsync(); } - public bool WaitForConfirms() - => InnerChannel.WaitForConfirms(); - public Task WaitForConfirmsAsync(CancellationToken token = default) => InnerChannel.WaitForConfirmsAsync(token); - public void WaitForConfirmsOrDie() - => InnerChannel.WaitForConfirmsOrDie(); - public Task WaitForConfirmsOrDieAsync(CancellationToken token = default) => InnerChannel.WaitForConfirmsOrDieAsync(token); diff --git a/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.Recovery.cs b/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.Recovery.cs index 28eab5f41f..d927592472 100644 --- a/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.Recovery.cs +++ b/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.Recovery.cs @@ -123,12 +123,12 @@ private void StopRecoveryLoop() private async ValueTask StopRecoveryLoopAsync() { Task? task = _recoveryTask; - if (task is not null) + if (task != null) { RecoveryCancellationTokenSource.Cancel(); try { - await task.TimeoutAfter(_config.RequestedConnectionTimeout) + await TaskExtensions.WaitAsync(task, _config.RequestedConnectionTimeout) .ConfigureAwait(false); } catch (TimeoutException) @@ -173,15 +173,12 @@ await _recordedEntitiesSemaphore.WaitAsync(cancellationToken) // 2. Recover queues // 3. Recover bindings // 4. Recover consumers - using (var recoveryChannelFactory = new RecoveryChannelFactory(_innerConnection)) - { - await RecoverExchangesAsync(recoveryChannelFactory, recordedEntitiesSemaphoreHeld: true) - .ConfigureAwait(false); - await RecoverQueuesAsync(recoveryChannelFactory, recordedEntitiesSemaphoreHeld: true) - .ConfigureAwait(false); - await RecoverBindingsAsync(recoveryChannelFactory, recordedEntitiesSemaphoreHeld: true) - .ConfigureAwait(false); - } + await RecoverExchangesAsync(_innerConnection, recordedEntitiesSemaphoreHeld: true) + .ConfigureAwait(false); + await RecoverQueuesAsync(_innerConnection, recordedEntitiesSemaphoreHeld: true) + .ConfigureAwait(false); + await RecoverBindingsAsync(_innerConnection, recordedEntitiesSemaphoreHeld: true) + .ConfigureAwait(false); } await RecoverChannelsAndItsConsumersAsync(recordedEntitiesSemaphoreHeld: true) @@ -214,7 +211,7 @@ await RecoverChannelsAndItsConsumersAsync(recordedEntitiesSemaphoreHeld: true) */ if (_innerConnection?.IsOpen == true) { - _innerConnection.Abort(Constants.InternalError, "FailedAutoRecovery", _config.RequestedConnectionTimeout); + await _innerConnection.AbortAsync(Constants.InternalError, "FailedAutoRecovery", _config.RequestedConnectionTimeout); } } catch (Exception e2) @@ -253,7 +250,7 @@ await _innerConnection.OpenAsync(cancellationToken) return false; } - private async ValueTask RecoverExchangesAsync(RecoveryChannelFactory recoveryChannelFactory, + private async ValueTask RecoverExchangesAsync(IConnection connection, bool recordedEntitiesSemaphoreHeld = false) { if (_disposed) @@ -270,21 +267,23 @@ await _innerConnection.OpenAsync(cancellationToken) { try { - using (IChannel ch = await recoveryChannelFactory.CreateRecoveryChannelAsync().ConfigureAwait(false)) + using (IChannel ch = await connection.CreateChannelAsync().ConfigureAwait(false)) { await recordedExchange.RecoverAsync(ch) .ConfigureAwait(false); + await ch.CloseAsync() + .ConfigureAwait(false); } } catch (Exception ex) { - if (_config.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionHandler != null + if (_config.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionHandlerAsync != null && _config.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionCondition(recordedExchange, ex)) { try { _recordedEntitiesSemaphore.Release(); - _config.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionHandler(recordedExchange, ex, this); + await _config.TopologyRecoveryExceptionHandler.ExchangeRecoveryExceptionHandlerAsync(recordedExchange, ex, this); } finally { @@ -300,7 +299,7 @@ await _recordedEntitiesSemaphore.WaitAsync() } } - private async Task RecoverQueuesAsync(RecoveryChannelFactory recoveryChannelFactory, + private async Task RecoverQueuesAsync(IConnection connection, bool recordedEntitiesSemaphoreHeld = false) { if (_disposed) @@ -318,10 +317,12 @@ await _recordedEntitiesSemaphore.WaitAsync() try { string newName = string.Empty; - using (IChannel ch = await recoveryChannelFactory.CreateRecoveryChannelAsync().ConfigureAwait(false)) + using (IChannel ch = await connection.CreateChannelAsync().ConfigureAwait(false)) { newName = await recordedQueue.RecoverAsync(ch) .ConfigureAwait(false); + await ch.CloseAsync() + .ConfigureAwait(false); } string oldName = recordedQueue.Name; @@ -360,13 +361,13 @@ await _recordedEntitiesSemaphore.WaitAsync() } catch (Exception ex) { - if (_config.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionHandler != null + if (_config.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionHandlerAsync != null && _config.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionCondition(recordedQueue, ex)) { try { _recordedEntitiesSemaphore.Release(); - _config.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionHandler(recordedQueue, ex, this); + await _config.TopologyRecoveryExceptionHandler.QueueRecoveryExceptionHandlerAsync(recordedQueue, ex, this); } finally { @@ -405,7 +406,7 @@ void UpdateConsumerQueue(string oldName, string newName) } } - private async ValueTask RecoverBindingsAsync(RecoveryChannelFactory recoveryChannelFactory, + private async ValueTask RecoverBindingsAsync(IConnection connection, bool recordedEntitiesSemaphoreHeld = false) { if (_disposed) @@ -422,21 +423,23 @@ void UpdateConsumerQueue(string oldName, string newName) { try { - using (IChannel ch = await recoveryChannelFactory.CreateRecoveryChannelAsync().ConfigureAwait(false)) + using (IChannel ch = await connection.CreateChannelAsync().ConfigureAwait(false)) { await binding.RecoverAsync(ch) .ConfigureAwait(false); + await ch.CloseAsync() + .ConfigureAwait(false); } } catch (Exception ex) { - if (_config.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionHandler != null + if (_config.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionHandlerAsync != null && _config.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionCondition(binding, ex)) { try { _recordedEntitiesSemaphore.Release(); - _config.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionHandler(binding, ex, this); + await _config.TopologyRecoveryExceptionHandler.BindingRecoveryExceptionHandlerAsync(binding, ex, this); } finally { @@ -506,13 +509,13 @@ await _recordedEntitiesSemaphore.WaitAsync() } catch (Exception ex) { - if (_config.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionHandler != null + if (_config.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionHandlerAsync != null && _config.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionCondition(consumer, ex)) { try { _recordedEntitiesSemaphore.Release(); - _config.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionHandler(consumer, ex, this); + await _config.TopologyRecoveryExceptionHandler.ConsumerRecoveryExceptionHandlerAsync(consumer, ex, this); } finally { @@ -549,44 +552,5 @@ private async ValueTask RecoverChannelsAndItsConsumersAsync(bool recordedEntitie .ConfigureAwait(false); } } - - private sealed class RecoveryChannelFactory : IDisposable - { - private readonly IConnection _connection; - private IChannel? _recoveryChannel; - - public RecoveryChannelFactory(IConnection connection) - { - _connection = connection; - } - - // TODO cancellation token - public async ValueTask CreateRecoveryChannelAsync() - { - if (_recoveryChannel == null) - { - _recoveryChannel = await _connection.CreateChannelAsync() - .ConfigureAwait(false); - } - - if (_recoveryChannel.IsClosed) - { - _recoveryChannel.Dispose(); - _recoveryChannel = await _connection.CreateChannelAsync() - .ConfigureAwait(false); - } - - return _recoveryChannel; - } - - public void Dispose() - { - if (_recoveryChannel != null) - { - _recoveryChannel.Close(); - _recoveryChannel.Dispose(); - } - } - } } } diff --git a/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs b/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs index 56962d8edc..fa6a01e0e2 100644 --- a/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs +++ b/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs @@ -85,7 +85,7 @@ await _innerConnection.OpenAsync(cancellationToken) private void CreateInnerConnection(IFrameHandler frameHandler) { - _innerConnection = new(_config, frameHandler); + _innerConnection = new Connection(_config, frameHandler); void onException(Exception exception, string context) => _innerConnection.OnCallbackException(CallbackExceptionEventArgs.Build(exception, context)); @@ -183,14 +183,6 @@ private void CreateInnerConnection(IFrameHandler frameHandler) public IProtocol Protocol => Endpoint.Protocol; - public RecoveryAwareChannel CreateNonRecoveringChannel() - { - ISession session = InnerConnection.CreateSession(); - var result = new RecoveryAwareChannel(_config, session); - result._Private_ChannelOpen(); - return result; - } - public async ValueTask CreateNonRecoveringChannelAsync() { ISession session = InnerConnection.CreateSession(); @@ -202,32 +194,21 @@ public async ValueTask CreateNonRecoveringChannelAsync() public override string ToString() => $"AutorecoveringConnection({InnerConnection.Id},{Endpoint},{GetHashCode()})"; - internal void CloseFrameHandler() + internal Task CloseFrameHandlerAsync() { - InnerConnection.FrameHandler.Close(); + return InnerConnection.FrameHandler.CloseAsync(); } ///API-side invocation of updating the secret. - public void UpdateSecret(string newSecret, string reason) + public Task UpdateSecretAsync(string newSecret, string reason) { ThrowIfDisposed(); EnsureIsOpen(); - _innerConnection.UpdateSecret(newSecret, reason); - } - - ///API-side invocation of connection.close with timeout. - public void Close(ushort reasonCode, string reasonText, TimeSpan timeout, bool abort) - { - ThrowIfDisposed(); - StopRecoveryLoop(); - if (_innerConnection.IsOpen) - { - _innerConnection.Close(reasonCode, reasonText, timeout, abort); - } + return _innerConnection.UpdateSecretAsync(newSecret, reason); } ///Asynchronous API-side invocation of connection.close with timeout. - public async ValueTask CloseAsync(ushort reasonCode, string reasonText, TimeSpan timeout, bool abort) + public async Task CloseAsync(ushort reasonCode, string reasonText, TimeSpan timeout, bool abort) { ThrowIfDisposed(); await StopRecoveryLoopAsync() @@ -239,16 +220,7 @@ await _innerConnection.CloseAsync(reasonCode, reasonText, timeout, abort) } } - public IChannel CreateChannel() - { - EnsureIsOpen(); - RecoveryAwareChannel recoveryAwareChannel = CreateNonRecoveringChannel(); - AutorecoveringChannel channel = new AutorecoveringChannel(this, recoveryAwareChannel); - RecordChannel(channel); - return channel; - } - - public async ValueTask CreateChannelAsync() + public async Task CreateChannelAsync() { EnsureIsOpen(); RecoveryAwareChannel recoveryAwareChannel = await CreateNonRecoveringChannelAsync() @@ -268,7 +240,12 @@ public void Dispose() try { - this.Abort(InternalConstants.DefaultConnectionAbortTimeout); + // TODO rabbitmq-dotnet-client-1472 + // this.Abort(InternalConstants.DefaultConnectionAbortTimeout); + if (IsOpen) + { + throw new InvalidOperationException("Connection must be closed before calling Dispose!"); + } } catch (Exception) { diff --git a/projects/RabbitMQ.Client/client/impl/ChannelBase.cs b/projects/RabbitMQ.Client/client/impl/ChannelBase.cs index 7f54b0eeb3..cadec4ea49 100644 --- a/projects/RabbitMQ.Client/client/impl/ChannelBase.cs +++ b/projects/RabbitMQ.Client/client/impl/ChannelBase.cs @@ -70,9 +70,15 @@ internal abstract class ChannelBase : IChannel, IRecoverable protected ChannelBase(ConnectionConfig config, ISession session) { ContinuationTimeout = config.ContinuationTimeout; - ConsumerDispatcher = config.DispatchConsumersAsync ? - new AsyncConsumerDispatcher(this, config.DispatchConsumerConcurrency) : - new ConsumerDispatcher(this, config.DispatchConsumerConcurrency); + + if (config.DispatchConsumersAsync) + { + ConsumerDispatcher = new AsyncConsumerDispatcher(this, config.DispatchConsumerConcurrency); + } + else + { + ConsumerDispatcher = new ConsumerDispatcher(this, config.DispatchConsumerConcurrency); + } Action onException = (exception, context) => OnCallbackException(CallbackExceptionEventArgs.Build(exception, context)); _basicAcksWrapper = new EventingWrapper("OnBasicAck", onException); @@ -240,13 +246,13 @@ public void Close(ushort replyCode, string replyText, bool abort) } } - public ValueTask CloseAsync(ushort replyCode, string replyText, bool abort) + public Task CloseAsync(ushort replyCode, string replyText, bool abort) { var args = new ShutdownEventArgs(ShutdownInitiator.Application, replyCode, replyText); return CloseAsync(args, abort); } - public async ValueTask CloseAsync(ShutdownEventArgs args, bool abort) + public async Task CloseAsync(ShutdownEventArgs args, bool abort) { using var k = new ChannelCloseAsyncRpcContinuation(ContinuationTimeout); await _rpcSemaphore.WaitAsync() @@ -383,7 +389,7 @@ protected void Enqueue(IRpcContinuation k) } } - internal async ValueTask OpenAsync() + internal async Task OpenAsync() { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); @@ -583,7 +589,11 @@ protected virtual void Dispose(bool disposing) if (disposing) { // dispose managed resources - this.Abort(); + // TODO exception? + if (IsOpen) + { + throw new InvalidOperationException("Channel must be closed before calling Dispose!"); + } ConsumerDispatcher.Dispose(); _rpcSemaphore.Dispose(); } @@ -967,7 +977,8 @@ protected void HandleConnectionStart(in IncomingCommand cmd) if (m_connectionStartCell is null) { var reason = new ShutdownEventArgs(ShutdownInitiator.Library, Constants.CommandInvalid, "Unexpected Connection.Start"); - Session.Connection.Close(reason, false, InternalConstants.DefaultConnectionCloseTimeout); + // TODO async! + Session.Connection.CloseAsync(reason, false, InternalConstants.DefaultConnectionCloseTimeout).EnsureCompleted(); } else { @@ -1034,126 +1045,51 @@ protected bool HandleQueueDeclareOk(in IncomingCommand cmd) } } - public abstract void _Private_BasicCancel(string consumerTag, bool nowait); - - public abstract void _Private_BasicConsume(string queue, string consumerTag, bool noLocal, bool autoAck, bool exclusive, bool nowait, IDictionary arguments); - - public abstract void _Private_BasicGet(string queue, bool autoAck); - public abstract void _Private_ChannelClose(ushort replyCode, string replyText, ushort classId, ushort methodId); public abstract void _Private_ChannelCloseOk(); public abstract void _Private_ChannelFlowOk(bool active); - public abstract void _Private_ChannelOpen(); - - public abstract void _Private_ConfirmSelect(bool nowait); - public abstract void _Private_ConnectionCloseOk(); - public abstract void _Private_UpdateSecret(byte[] @newSecret, string @reason); - - public abstract void _Private_ExchangeBind(string destination, string source, string routingKey, bool nowait, IDictionary arguments); - - public abstract void _Private_ExchangeDeclare(string exchange, string type, bool passive, bool durable, bool autoDelete, bool @internal, bool nowait, IDictionary arguments); - - public abstract void _Private_ExchangeDelete(string exchange, bool ifUnused, bool nowait); - - public abstract void _Private_ExchangeUnbind(string destination, string source, string routingKey, bool nowait, IDictionary arguments); - - public abstract void _Private_QueueBind(string queue, string exchange, string routingKey, bool nowait, IDictionary arguments); - - public abstract void _Private_QueueDeclare(string queue, bool passive, bool durable, bool exclusive, bool autoDelete, bool nowait, IDictionary arguments); - - public abstract uint _Private_QueueDelete(string queue, bool ifUnused, bool ifEmpty, bool nowait); - - public abstract uint _Private_QueuePurge(string queue, bool nowait); - - public abstract void BasicAck(ulong deliveryTag, bool multiple); - public abstract ValueTask BasicAckAsync(ulong deliveryTag, bool multiple); - public void BasicCancel(string consumerTag) - { - var k = new BasicConsumeRpcContinuation { m_consumerTag = consumerTag }; - _rpcSemaphore.Wait(); - try - { - Enqueue(k); - _Private_BasicCancel(consumerTag, false); - k.GetReply(ContinuationTimeout); - } - finally - { - _rpcSemaphore.Release(); - } - } - - public async ValueTask BasicCancelAsync(string consumerTag) + public async Task BasicCancelAsync(string consumerTag, bool noWait) { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); try { - using var k = new BasicCancelAsyncRpcContinuation(consumerTag, ConsumerDispatcher, ContinuationTimeout); - Enqueue(k); - - var method = new Client.Framing.Impl.BasicCancel(consumerTag, false); - await ModelSendAsync(method) - .ConfigureAwait(false); + var method = new Client.Framing.Impl.BasicCancel(consumerTag, noWait); - bool result = await k; - Debug.Assert(result); - return; - } - finally - { - _rpcSemaphore.Release(); - } - } - - public void BasicCancelNoWait(string consumerTag) - { - _Private_BasicCancel(consumerTag, true); - ConsumerDispatcher.GetAndRemoveConsumer(consumerTag); - } - - public string BasicConsume(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, - IDictionary arguments, IBasicConsumer consumer) - { - // TODO: Replace with flag - if (ConsumerDispatcher is AsyncConsumerDispatcher) - { - if (!(consumer is IAsyncBasicConsumer)) + if (noWait) { - // TODO: Friendly message - throw new InvalidOperationException("In the async mode you have to use an async consumer"); + await ModelSendAsync(method) + .ConfigureAwait(false); + ConsumerDispatcher.GetAndRemoveConsumer(consumerTag); } - } + else + { + using var k = new BasicCancelAsyncRpcContinuation(consumerTag, ConsumerDispatcher, ContinuationTimeout); + Enqueue(k); - var k = new BasicConsumeRpcContinuation { m_consumer = consumer }; + await ModelSendAsync(method) + .ConfigureAwait(false); - _rpcSemaphore.Wait(); - try - { - Enqueue(k); - // Non-nowait. We have an unconventional means of getting - // the RPC response, but a response is still expected. - _Private_BasicConsume(queue, consumerTag, noLocal, autoAck, exclusive, false, arguments); - k.GetReply(ContinuationTimeout); + bool result = await k; + Debug.Assert(result); + } + + return; } finally { _rpcSemaphore.Release(); } - - string actualConsumerTag = k.m_consumerTag; - - return actualConsumerTag; } - public async ValueTask BasicConsumeAsync(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, + public async Task BasicConsumeAsync(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, IDictionary arguments, IBasicConsumer consumer) { // TODO: Replace with flag @@ -1185,25 +1121,6 @@ await ModelSendAsync(method) } } - public BasicGetResult BasicGet(string queue, bool autoAck) - { - var k = new BasicGetRpcContinuation(); - - _rpcSemaphore.Wait(); - try - { - Enqueue(k); - _Private_BasicGet(queue, autoAck); - k.GetReply(ContinuationTimeout); - } - finally - { - _rpcSemaphore.Release(); - } - - return k.m_result; - } - public async ValueTask BasicGetAsync(string queue, bool autoAck) { await _rpcSemaphore.WaitAsync() @@ -1225,72 +1142,8 @@ await ModelSendAsync(method) } } - public abstract void BasicNack(ulong deliveryTag, bool multiple, bool requeue); - public abstract ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue); - public void BasicPublish(string exchange, string routingKey, in TProperties basicProperties, ReadOnlyMemory body, bool mandatory) - where TProperties : IReadOnlyBasicProperties, IAmqpHeader - { - if (NextPublishSeqNo > 0) - { - lock (_confirmLock) - { - _pendingDeliveryTags.AddLast(NextPublishSeqNo++); - } - } - - try - { - var cmd = new BasicPublish(exchange, routingKey, mandatory, default); - ChannelSend(in cmd, in basicProperties, body); - } - catch - { - if (NextPublishSeqNo > 0) - { - lock (_confirmLock) - { - NextPublishSeqNo--; - _pendingDeliveryTags.RemoveLast(); - } - } - - throw; - } - } - - public void BasicPublish(CachedString exchange, CachedString routingKey, in TProperties basicProperties, ReadOnlyMemory body, bool mandatory) - where TProperties : IReadOnlyBasicProperties, IAmqpHeader - { - if (NextPublishSeqNo > 0) - { - lock (_confirmLock) - { - _pendingDeliveryTags.AddLast(NextPublishSeqNo++); - } - } - - try - { - var cmd = new BasicPublishMemory(exchange.Bytes, routingKey.Bytes, mandatory, default); - ChannelSend(in cmd, in basicProperties, body); - } - catch - { - if (NextPublishSeqNo > 0) - { - lock (_confirmLock) - { - NextPublishSeqNo--; - _pendingDeliveryTags.RemoveLast(); - } - } - - throw; - } - } - public ValueTask BasicPublishAsync(string exchange, string routingKey, in TProperties basicProperties, ReadOnlyMemory body, bool mandatory) where TProperties : IReadOnlyBasicProperties, IAmqpHeader { @@ -1353,7 +1206,7 @@ public ValueTask BasicPublishAsync(CachedString exchange, CachedStr } } - public void UpdateSecret(string newSecret, string reason) + public async Task UpdateSecretAsync(string newSecret, string reason) { if (newSecret is null) { @@ -1365,21 +1218,15 @@ public void UpdateSecret(string newSecret, string reason) throw new ArgumentNullException(nameof(reason)); } - _Private_UpdateSecret(Encoding.UTF8.GetBytes(newSecret), reason); - } - - public abstract void BasicQos(uint prefetchSize, ushort prefetchCount, bool global); - - public async ValueTask BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global) - { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); try { - using var k = new BasicQosAsyncRpcContinuation(ContinuationTimeout); + using var k = new SimpleAsyncRpcContinuation(ProtocolCommandId.ConnectionUpdateSecretOk, ContinuationTimeout); Enqueue(k); - var method = new BasicQos(prefetchSize, prefetchCount, global); + var newSecretBytes = Encoding.UTF8.GetBytes(newSecret); + var method = new ConnectionUpdateSecret(newSecretBytes, reason); await ModelSendAsync(method) .ConfigureAwait(false); @@ -1393,22 +1240,32 @@ await ModelSendAsync(method) } } - public abstract void BasicReject(ulong deliveryTag, bool requeue); + public async Task BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global) + { + await _rpcSemaphore.WaitAsync() + .ConfigureAwait(false); + try + { + using var k = new BasicQosAsyncRpcContinuation(ContinuationTimeout); + Enqueue(k); - public abstract ValueTask BasicRejectAsync(ulong deliveryTag, bool requeue); + var method = new BasicQos(prefetchSize, prefetchCount, global); + await ModelSendAsync(method) + .ConfigureAwait(false); - public void ConfirmSelect() - { - if (NextPublishSeqNo == 0UL) + bool result = await k; + Debug.Assert(result); + return; + } + finally { - _confirmsTaskCompletionSources = new List>(); - NextPublishSeqNo = 1; + _rpcSemaphore.Release(); } - - _Private_ConfirmSelect(false); } - public async ValueTask ConfirmSelectAsync() + public abstract Task BasicRejectAsync(ulong deliveryTag, bool requeue); + + public async Task ConfirmSelectAsync() { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); @@ -1438,26 +1295,32 @@ await ModelSendAsync(method) } } - public void ExchangeBind(string destination, string source, string routingKey, IDictionary arguments) - { - _Private_ExchangeBind(destination, source, routingKey, false, arguments); - } - - public async ValueTask ExchangeBindAsync(string destination, string source, string routingKey, IDictionary arguments) + public async Task ExchangeBindAsync(string destination, string source, string routingKey, + IDictionary arguments, bool noWait) { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); try { - using var k = new ExchangeBindAsyncRpcContinuation(ContinuationTimeout); - Enqueue(k); + var method = new ExchangeBind(destination, source, routingKey, noWait, arguments); - var method = new ExchangeBind(destination, source, routingKey, false, arguments); - await ModelSendAsync(method) - .ConfigureAwait(false); + if (noWait) + { + await ModelSendAsync(method) + .ConfigureAwait(false); + } + else + { + using var k = new ExchangeBindAsyncRpcContinuation(ContinuationTimeout); + Enqueue(k); + + await ModelSendAsync(method) + .ConfigureAwait(false); + + bool result = await k; + Debug.Assert(result); + } - bool result = await k; - Debug.Assert(result); return; } finally @@ -1466,31 +1329,38 @@ await ModelSendAsync(method) } } - public void ExchangeBindNoWait(string destination, string source, string routingKey, IDictionary arguments) + public Task ExchangeDeclarePassiveAsync(string exchange) { - _Private_ExchangeBind(destination, source, routingKey, true, arguments); + return ExchangeDeclareAsync(exchange: exchange, type: string.Empty, passive: true, + durable: false, autoDelete: false, arguments: null, noWait: false); } - public void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments) - { - _Private_ExchangeDeclare(exchange, type, false, durable, autoDelete, false, false, arguments); - } - - public async ValueTask ExchangeDeclareAsync(string exchange, string type, bool passive, bool durable, bool autoDelete, IDictionary arguments) + public async Task ExchangeDeclareAsync(string exchange, string type, bool durable, bool autoDelete, + IDictionary arguments, bool passive, bool noWait) { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); try { - using var k = new ExchangeDeclareAsyncRpcContinuation(ContinuationTimeout); - Enqueue(k); + var method = new ExchangeDeclare(exchange, type, passive, durable, autoDelete, false, noWait, arguments); - var method = new ExchangeDeclare(exchange, type, passive, durable, autoDelete, false, false, arguments); - await ModelSendAsync(method) - .ConfigureAwait(false); + if (noWait) + { + await ModelSendAsync(method) + .ConfigureAwait(false); + } + else + { + using var k = new ExchangeDeclareAsyncRpcContinuation(ContinuationTimeout); + Enqueue(k); + + await ModelSendAsync(method) + .ConfigureAwait(false); + + bool result = await k; + Debug.Assert(result); + } - bool result = await k; - Debug.Assert(result); return; } finally @@ -1499,36 +1369,31 @@ await ModelSendAsync(method) } } - public void ExchangeDeclareNoWait(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments) - { - _Private_ExchangeDeclare(exchange, type, false, durable, autoDelete, false, true, arguments); - } - - public void ExchangeDeclarePassive(string exchange) + public async Task ExchangeDeleteAsync(string exchange, bool ifUnused, bool noWait) { - _Private_ExchangeDeclare(exchange, "", true, false, false, false, false, null); - } - - public void ExchangeDelete(string exchange, bool ifUnused) - { - _Private_ExchangeDelete(exchange, ifUnused, false); - } - - public async ValueTask ExchangeDeleteAsync(string exchange, bool ifUnused) - { - using var k = new ExchangeDeleteAsyncRpcContinuation(ContinuationTimeout); await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); try { - Enqueue(k); + var method = new ExchangeDelete(exchange, ifUnused, Nowait: noWait); - var method = new ExchangeDelete(exchange, ifUnused, Nowait: false); - await ModelSendAsync(method) - .ConfigureAwait(false); + if (noWait) + { + await ModelSendAsync(method) + .ConfigureAwait(false); + } + else + { + using var k = new ExchangeDeleteAsyncRpcContinuation(ContinuationTimeout); + Enqueue(k); + + await ModelSendAsync(method) + .ConfigureAwait(false); + + bool result = await k; + Debug.Assert(result); + } - bool result = await k; - Debug.Assert(result); return; } finally @@ -1537,31 +1402,32 @@ await ModelSendAsync(method) } } - public void ExchangeDeleteNoWait(string exchange, bool ifUnused) - { - _Private_ExchangeDelete(exchange, ifUnused, true); - } - - public void ExchangeUnbind(string destination, string source, string routingKey, IDictionary arguments) - { - _Private_ExchangeUnbind(destination, source, routingKey, false, arguments); - } - - public async ValueTask ExchangeUnbindAsync(string destination, string source, string routingKey, IDictionary arguments) + public async Task ExchangeUnbindAsync(string destination, string source, string routingKey, + IDictionary arguments, bool noWait) { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); try { - using var k = new ExchangeUnbindAsyncRpcContinuation(ContinuationTimeout); - Enqueue(k); + var method = new ExchangeUnbind(destination, source, routingKey, noWait, arguments); - var method = new ExchangeUnbind(destination, source, routingKey, false, arguments); - await ModelSendAsync(method) - .ConfigureAwait(false); + if (noWait) + { + await ModelSendAsync(method) + .ConfigureAwait(false); + } + else + { + using var k = new ExchangeUnbindAsyncRpcContinuation(ContinuationTimeout); + Enqueue(k); + + await ModelSendAsync(method) + .ConfigureAwait(false); + + bool result = await k; + Debug.Assert(result); + } - bool result = await k; - Debug.Assert(result); return; } finally @@ -1570,45 +1436,63 @@ await ModelSendAsync(method) } } - public void ExchangeUnbindNoWait(string destination, string source, string routingKey, IDictionary arguments) + public Task QueueDeclarePassiveAsync(string queue) { - _Private_ExchangeUnbind(destination, source, routingKey, true, arguments); + return QueueDeclareAsync(queue: queue, passive: true, + durable: false, exclusive: false, autoDelete: false, + noWait: false, arguments: null); } - public void QueueBind(string queue, string exchange, string routingKey, IDictionary arguments) + public async Task QueueDeclareAsync(string queue, bool durable, bool exclusive, bool autoDelete, + IDictionary arguments, bool passive, bool noWait) { - _Private_QueueBind(queue, exchange, routingKey, false, arguments); - } - - public void QueueBindNoWait(string queue, string exchange, string routingKey, IDictionary arguments) - { - _Private_QueueBind(queue, exchange, routingKey, true, arguments); - } + if (true == noWait) + { + if (queue == string.Empty) + { + throw new InvalidOperationException("noWait must not be used with a server-named queue."); + } - public QueueDeclareOk QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) - { - return DoQueueDeclare(queue, false, durable, exclusive, autoDelete, arguments); - } + if (true == passive) + { + throw new InvalidOperationException("It does not make sense to use noWait: true and passive: true"); + } + } - public async ValueTask QueueDeclareAsync(string queue, bool passive, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) - { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); try { - using var k = new QueueDeclareAsyncRpcContinuation(ContinuationTimeout); - Enqueue(k); + var method = new QueueDeclare(queue, passive, durable, exclusive, autoDelete, noWait, arguments); - var method = new QueueDeclare(queue, passive, durable, exclusive, autoDelete, false, arguments); - await ModelSendAsync(method) - .ConfigureAwait(false); + if (noWait) + { + await ModelSendAsync(method) + .ConfigureAwait(false); + + if (false == passive) + { + CurrentQueue = queue; + } - QueueDeclareOk result = await k; - if (false == passive) + return new QueueDeclareOk(queue, 0, 0); + } + else { - CurrentQueue = result.QueueName; + using var k = new QueueDeclareAsyncRpcContinuation(ContinuationTimeout); + Enqueue(k); + + await ModelSendAsync(method) + .ConfigureAwait(false); + + QueueDeclareOk result = await k; + if (false == passive) + { + CurrentQueue = result.QueueName; + } + + return result; } - return result; } finally { @@ -1616,21 +1500,32 @@ await ModelSendAsync(method) } } - public async ValueTask QueueBindAsync(string queue, string exchange, string routingKey, IDictionary arguments) + public async Task QueueBindAsync(string queue, string exchange, string routingKey, + IDictionary arguments, bool noWait) { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); try { - using var k = new QueueBindAsyncRpcContinuation(ContinuationTimeout); - Enqueue(k); + var method = new QueueBind(queue, exchange, routingKey, noWait, arguments); - var method = new QueueBind(queue, exchange, routingKey, false, arguments); - await ModelSendAsync(method) - .ConfigureAwait(false); + if (noWait) + { + await ModelSendAsync(method) + .ConfigureAwait(false); + } + else + { + using var k = new QueueBindAsyncRpcContinuation(ContinuationTimeout); + Enqueue(k); + + await ModelSendAsync(method) + .ConfigureAwait(false); + + bool result = await k; + Debug.Assert(result); + } - bool result = await k; - Debug.Assert(result); return; } finally @@ -1639,47 +1534,43 @@ await ModelSendAsync(method) } } - public void QueueDeclareNoWait(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) - { - _Private_QueueDeclare(queue, false, durable, exclusive, autoDelete, true, arguments); - } - - public QueueDeclareOk QueueDeclarePassive(string queue) + public async Task MessageCountAsync(string queue) { - return DoQueueDeclare(queue, true, false, false, false, null); - } - - public uint MessageCount(string queue) - { - QueueDeclareOk ok = QueueDeclarePassive(queue); + QueueDeclareOk ok = await QueueDeclarePassiveAsync(queue); return ok.MessageCount; } - public uint ConsumerCount(string queue) + public async Task ConsumerCountAsync(string queue) { - QueueDeclareOk ok = QueueDeclarePassive(queue); + QueueDeclareOk ok = await QueueDeclarePassiveAsync(queue); return ok.ConsumerCount; } - public uint QueueDelete(string queue, bool ifUnused, bool ifEmpty) - { - return _Private_QueueDelete(queue, ifUnused, ifEmpty, false); - } - - public async ValueTask QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty) + public async Task QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty, bool noWait) { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); try { - var k = new QueueDeleteAsyncRpcContinuation(ContinuationTimeout); - Enqueue(k); + var method = new QueueDelete(queue, ifUnused, ifEmpty, noWait); - var method = new QueueDelete(queue, ifUnused, ifEmpty, false); - await ModelSendAsync(method) - .ConfigureAwait(false); + if (noWait) + { + await ModelSendAsync(method) + .ConfigureAwait(false); - return await k; + return 0; + } + else + { + var k = new QueueDeleteAsyncRpcContinuation(ContinuationTimeout); + Enqueue(k); + + await ModelSendAsync(method) + .ConfigureAwait(false); + + return await k; + } } finally { @@ -1687,17 +1578,7 @@ await ModelSendAsync(method) } } - public void QueueDeleteNoWait(string queue, bool ifUnused, bool ifEmpty) - { - _Private_QueueDelete(queue, ifUnused, ifEmpty, true); - } - - public uint QueuePurge(string queue) - { - return _Private_QueuePurge(queue, false); - } - - public async ValueTask QueuePurgeAsync(string queue) + public async Task QueuePurgeAsync(string queue) { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); @@ -1718,9 +1599,7 @@ await ModelSendAsync(method) } } - public abstract void QueueUnbind(string queue, string exchange, string routingKey, IDictionary arguments); - - public async ValueTask QueueUnbindAsync(string queue, string exchange, string routingKey, IDictionary arguments) + public async Task QueueUnbindAsync(string queue, string exchange, string routingKey, IDictionary arguments) { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); @@ -1743,9 +1622,7 @@ await ModelSendAsync(method) } } - public abstract void TxCommit(); - - public async ValueTask TxCommitAsync() + public async Task TxCommitAsync() { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); @@ -1768,9 +1645,7 @@ await ModelSendAsync(method) } } - public abstract void TxRollback(); - - public async ValueTask TxRollbackAsync() + public async Task TxRollbackAsync() { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); @@ -1793,9 +1668,7 @@ await ModelSendAsync(method) } } - public abstract void TxSelect(); - - public async ValueTask TxSelectAsync() + public async Task TxSelectAsync() { await _rpcSemaphore.WaitAsync() .ConfigureAwait(false); @@ -1820,11 +1693,6 @@ await ModelSendAsync(method) private List> _confirmsTaskCompletionSources; - public bool WaitForConfirms() - { - return WaitForConfirmsAsync().EnsureCompleted(); - } - public Task WaitForConfirmsAsync(CancellationToken token = default) { if (NextPublishSeqNo == 0UL) @@ -1882,11 +1750,6 @@ await tokenRegistration.DisposeAsync() } } - public void WaitForConfirmsOrDie() - { - WaitForConfirmsOrDieAsync().EnsureCompleted(); - } - public async Task WaitForConfirmsOrDieAsync(CancellationToken token = default) { try @@ -1917,26 +1780,5 @@ await CloseAsync(ea, false) throw ex; } } - - private QueueDeclareOk DoQueueDeclare(string queue, bool passive, bool durable, bool exclusive, bool autoDelete, IDictionary arguments) - { - var k = new QueueDeclareRpcContinuation(); - - _rpcSemaphore.Wait(); - try - { - Enqueue(k); - _Private_QueueDeclare(queue, passive, durable, exclusive, autoDelete, false, arguments); - k.GetReply(ContinuationTimeout); - } - finally - { - _rpcSemaphore.Release(); - } - - QueueDeclareOk result = k.m_result; - CurrentQueue = result.QueueName; - return result; - } } } diff --git a/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs b/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs index ce92007fff..ae2991945e 100644 --- a/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs +++ b/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs @@ -43,9 +43,9 @@ namespace RabbitMQ.Client.Framing.Impl #nullable enable internal sealed partial class Connection { - public void UpdateSecret(string newSecret, string reason) + public Task UpdateSecretAsync(string newSecret, string reason) { - _channel0.UpdateSecret(newSecret, reason); + return _channel0.UpdateSecretAsync(newSecret, reason); } internal void NotifyReceivedCloseOk() @@ -186,11 +186,15 @@ private void MaybeStartCredentialRefresher() } } - private void NotifyCredentialRefreshed(bool succesfully) + private Task NotifyCredentialRefreshed(bool succesfully) { if (succesfully) { - UpdateSecret(_config.CredentialsProvider.Password, "Token refresh"); + return UpdateSecretAsync(_config.CredentialsProvider.Password, "Token refresh"); + } + else + { + return Task.CompletedTask; } } diff --git a/projects/RabbitMQ.Client/client/impl/Connection.cs b/projects/RabbitMQ.Client/client/impl/Connection.cs index 4417d991e3..0645850342 100644 --- a/projects/RabbitMQ.Client/client/impl/Connection.cs +++ b/projects/RabbitMQ.Client/client/impl/Connection.cs @@ -253,16 +253,7 @@ await CloseAsync(ea, true, TimeSpan.FromSeconds(5)) } } - public IChannel CreateChannel() - { - EnsureIsOpen(); - ISession session = CreateSession(); - var channel = new Channel(_config, session); - channel._Private_ChannelOpen(); - return channel; - } - - public ValueTask CreateChannelAsync() + public Task CreateChannelAsync() { EnsureIsOpen(); ISession session = CreateSession(); @@ -290,99 +281,12 @@ internal void EnsureIsOpen() } } - ///API-side invocation of connection.close with timeout. - public void Close(ushort reasonCode, string reasonText, TimeSpan timeout, bool abort) - { - Close(new ShutdownEventArgs(ShutdownInitiator.Application, reasonCode, reasonText), abort, timeout); - } - ///Asynchronous API-side invocation of connection.close with timeout. - public ValueTask CloseAsync(ushort reasonCode, string reasonText, TimeSpan timeout, bool abort) + public Task CloseAsync(ushort reasonCode, string reasonText, TimeSpan timeout, bool abort) { return CloseAsync(new ShutdownEventArgs(ShutdownInitiator.Application, reasonCode, reasonText), abort, timeout); } - ///Try to close connection in a graceful way - /// - /// - ///Shutdown reason contains code and text assigned when closing the connection, - ///as well as the information about what initiated the close - /// - /// - ///Abort flag, if true, signals to close the ongoing connection immediately - ///and do not report any errors if it was already closed. - /// - /// - ///Timeout determines how much time internal close operations should be given - ///to complete. - /// - /// - internal void Close(ShutdownEventArgs reason, bool abort, TimeSpan timeout) - { - if (!SetCloseReason(reason)) - { - if (!abort) - { - ThrowAlreadyClosedException(CloseReason!); - } - } - else - { - OnShutdown(reason); - _session0.SetSessionClosing(false); - - try - { - // Try to send connection.close wait for CloseOk in the MainLoop - if (!_closed) - { - _session0.Transmit(new ConnectionClose(reason.ReplyCode, reason.ReplyText, 0, 0)); - } - } - catch (AlreadyClosedException) - { - if (!abort) - { - throw; - } - } - catch (NotSupportedException) - { - // buffered stream had unread data in it and Flush() - // was called, ignore to not confuse the user - } - catch (IOException ioe) - { - if (_channel0.CloseReason is null) - { - if (!abort) - { - throw; - } - else - { - LogCloseError("Couldn't close connection cleanly. Socket closed unexpectedly", ioe); - } - } - } - finally - { - TerminateMainloop(); - } - } - - try - { - if (!_mainLoopTask.Wait(timeout)) - { - _frameHandler.Close(); - } - } - catch (AggregateException) // TODO this could be more than just a timeout - { - } - } - ///Asychronously try to close connection in a graceful way /// /// @@ -399,12 +303,12 @@ internal void Close(ShutdownEventArgs reason, bool abort, TimeSpan timeout) /// /// // TODO cancellation token - internal async ValueTask CloseAsync(ShutdownEventArgs reason, bool abort, TimeSpan timeout) + internal async Task CloseAsync(ShutdownEventArgs reason, bool abort, TimeSpan timeout) { - // TODO CloseAsync and Close share a lot of code - if (!SetCloseReason(reason)) + if (false == SetCloseReason(reason)) { - if (!abort) + // close reason is already set + if (false == abort) { ThrowAlreadyClosedException(CloseReason!); } @@ -458,7 +362,7 @@ await _session0.TransmitAsync(method) try { - await _mainLoopTask.TimeoutAfter(timeout) + await _mainLoopTask.WaitAsync(timeout) .ConfigureAwait(false); } catch (TimeoutException) @@ -562,8 +466,15 @@ public void Dispose() try { - this.Abort(InternalConstants.DefaultConnectionAbortTimeout); - _mainLoopTask.Wait(); + /* + * TODO rabbitmq-dotnet-client-1472 + * this.Abort(InternalConstants.DefaultConnectionAbortTimeout); + * _mainLoopTask.Wait(); + */ + if (IsOpen) + { + throw new InvalidOperationException("Connection must be closed before calling Dispose!"); + } } catch (OperationInterruptedException) { diff --git a/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcher.cs b/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcher.cs index 380d95be6c..17ac041d93 100644 --- a/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcher.cs +++ b/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcher.cs @@ -29,7 +29,7 @@ protected override async Task ProcessChannelAsync(CancellationToken token) switch (work.WorkType) { case WorkType.Deliver: - consumer.HandleBasicDeliver( + await consumer.HandleBasicDeliverAsync( consumerTag, work.DeliveryTag, work.Redelivered, work.Exchange, work.RoutingKey, work.BasicProperties, work.Body.Memory); break; diff --git a/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcherBase.cs b/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcherBase.cs index d61a783544..eb85a04e08 100644 --- a/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcherBase.cs +++ b/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcherBase.cs @@ -7,8 +7,8 @@ namespace RabbitMQ.Client.ConsumerDispatching #nullable enable internal abstract class ConsumerDispatcherBase { - private static readonly FallbackConsumer fallbackConsumer = new(); - private readonly Dictionary _consumers = new(); + private static readonly FallbackConsumer fallbackConsumer = new FallbackConsumer(); + private readonly Dictionary _consumers = new Dictionary(); public IBasicConsumer? DefaultConsumer { get; set; } diff --git a/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcherChannelBase.cs b/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcherChannelBase.cs index 06682b2c6c..1c5811969a 100644 --- a/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcherChannelBase.cs +++ b/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/ConsumerDispatcherChannelBase.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; @@ -13,7 +11,7 @@ namespace RabbitMQ.Client.ConsumerDispatching #nullable enable internal abstract class ConsumerDispatcherChannelBase : ConsumerDispatcherBase, IConsumerDispatcher { - protected readonly CancellationTokenSource _consumerDispatcherCts = new(); + protected readonly CancellationTokenSource _consumerDispatcherCts = new CancellationTokenSource(); protected readonly CancellationToken _consumerDispatcherToken; protected readonly ChannelBase _channel; @@ -140,13 +138,21 @@ public void WaitForShutdown() catch (AggregateException aex) { AggregateException aexf = aex.Flatten(); - IEnumerable nonTaskCanceled = aexf.InnerExceptions.Where(iex => iex is not TaskCanceledException); - if (nonTaskCanceled.Any()) + bool foundUnexpectedException = false; + foreach (Exception innerAexf in aexf.InnerExceptions) + { + if (false == (innerAexf is OperationCanceledException)) + { + foundUnexpectedException = true; + break; + } + } + if (foundUnexpectedException) { ESLog.Warn("consumer dispatcher task had unexpected exceptions"); } } - catch (TaskCanceledException) + catch (OperationCanceledException) { } } @@ -176,13 +182,21 @@ await _worker catch (AggregateException aex) { AggregateException aexf = aex.Flatten(); - IEnumerable nonTaskCanceled = aexf.InnerExceptions.Where(iex => iex is not TaskCanceledException); - if (nonTaskCanceled.Any()) + bool foundUnexpectedException = false; + foreach (Exception innerAexf in aexf.InnerExceptions) + { + if (false == (innerAexf is OperationCanceledException)) + { + foundUnexpectedException = true; + break; + } + } + if (foundUnexpectedException) { ESLog.Warn("consumer dispatcher task had unexpected exceptions (async)"); } } - catch (TaskCanceledException) + catch (OperationCanceledException) { } } diff --git a/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/FallbackConsumer.cs b/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/FallbackConsumer.cs index b901a4ae71..dd9ae73852 100644 --- a/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/FallbackConsumer.cs +++ b/projects/RabbitMQ.Client/client/impl/ConsumerDispatching/FallbackConsumer.cs @@ -37,10 +37,11 @@ void IBasicConsumer.HandleBasicConsumeOk(string consumerTag) ESLog.Info($"Unhandled {nameof(IBasicConsumer.HandleBasicConsumeOk)} for tag {consumerTag}"); } - void IBasicConsumer.HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - in ReadOnlyBasicProperties properties, ReadOnlyMemory body) + Task IBasicConsumer.HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, + ReadOnlyBasicProperties properties, ReadOnlyMemory body) { - ESLog.Info($"Unhandled {nameof(IBasicConsumer.HandleBasicDeliver)} for tag {consumerTag}"); + ESLog.Info($"Unhandled {nameof(IBasicConsumer.HandleBasicDeliverAsync)} for tag {consumerTag}"); + return Task.CompletedTask; } void IBasicConsumer.HandleChannelShutdown(object channel, ShutdownEventArgs reason) @@ -69,8 +70,7 @@ Task IAsyncBasicConsumer.HandleBasicConsumeOk(string consumerTag) Task IAsyncBasicConsumer.HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, in ReadOnlyBasicProperties properties, ReadOnlyMemory body) { - ((IBasicConsumer)this).HandleBasicDeliver(consumerTag, deliveryTag, redelivered, exchange, routingKey, properties, body); - return Task.CompletedTask; + return ((IBasicConsumer)this).HandleBasicDeliverAsync(consumerTag, deliveryTag, redelivered, exchange, routingKey, properties, body); } Task IAsyncBasicConsumer.HandleChannelShutdown(object channel, ShutdownEventArgs reason) diff --git a/projects/RabbitMQ.Client/client/impl/RecordedBinding.cs b/projects/RabbitMQ.Client/client/impl/RecordedBinding.cs index 1d3ed4221f..d0a861d8fd 100644 --- a/projects/RabbitMQ.Client/client/impl/RecordedBinding.cs +++ b/projects/RabbitMQ.Client/client/impl/RecordedBinding.cs @@ -67,15 +67,15 @@ public RecordedBinding(string destination, in RecordedBinding old) _arguments = old._arguments; } - public ValueTask RecoverAsync(IChannel channel) + public Task RecoverAsync(IChannel channel) { if (_isQueueBinding) { - return channel.QueueBindAsync(_destination, _source, _routingKey, _arguments); + return channel.QueueBindAsync(_destination, _source, _routingKey, _arguments, false); } else { - return channel.ExchangeBindAsync(_destination, _source, _routingKey, _arguments); + return channel.ExchangeBindAsync(_destination, _source, _routingKey, _arguments, false); } } diff --git a/projects/RabbitMQ.Client/client/impl/RecordedConsumer.cs b/projects/RabbitMQ.Client/client/impl/RecordedConsumer.cs index 6cb909967f..888635f0a9 100644 --- a/projects/RabbitMQ.Client/client/impl/RecordedConsumer.cs +++ b/projects/RabbitMQ.Client/client/impl/RecordedConsumer.cs @@ -105,7 +105,7 @@ public static RecordedConsumer WithNewQueueName(string newQueueName, in Recorded return new RecordedConsumer(old.Channel, old.Consumer, old.ConsumerTag, newQueueName, old.AutoAck, old.Exclusive, old.Arguments); } - public ValueTask RecoverAsync(IChannel channel) + public Task RecoverAsync(IChannel channel) { return channel.BasicConsumeAsync(Queue, AutoAck, ConsumerTag, false, Exclusive, Arguments, Consumer); } diff --git a/projects/RabbitMQ.Client/client/impl/RecordedExchange.cs b/projects/RabbitMQ.Client/client/impl/RecordedExchange.cs index d5aeeb646d..a7b1c1d946 100644 --- a/projects/RabbitMQ.Client/client/impl/RecordedExchange.cs +++ b/projects/RabbitMQ.Client/client/impl/RecordedExchange.cs @@ -58,10 +58,10 @@ public RecordedExchange(string name, string type, bool durable, bool autoDelete, _arguments = arguments; } - public ValueTask RecoverAsync(IChannel channel) + public Task RecoverAsync(IChannel channel) { return channel.ExchangeDeclareAsync(exchange: Name, type: _type, passive: false, - durable: _durable, autoDelete: AutoDelete, arguments: _arguments); + durable: _durable, autoDelete: AutoDelete, noWait: false, arguments: _arguments); } public override string ToString() diff --git a/projects/RabbitMQ.Client/client/impl/RecordedQueue.cs b/projects/RabbitMQ.Client/client/impl/RecordedQueue.cs index 43f3f19a9e..d491f98280 100644 --- a/projects/RabbitMQ.Client/client/impl/RecordedQueue.cs +++ b/projects/RabbitMQ.Client/client/impl/RecordedQueue.cs @@ -71,7 +71,7 @@ public RecordedQueue(string newName, in RecordedQueue old) _arguments = old._arguments; } - public ValueTask RecoverAsync(IChannel channel) + public Task RecoverAsync(IChannel channel) { string queueName = IsServerNamed ? string.Empty : Name; return channel.QueueDeclareAsync(queue: queueName, passive: false, diff --git a/projects/RabbitMQ.Client/client/impl/RecoveryAwareChannel.cs b/projects/RabbitMQ.Client/client/impl/RecoveryAwareChannel.cs index 49a3294d0b..7c436e881a 100644 --- a/projects/RabbitMQ.Client/client/impl/RecoveryAwareChannel.cs +++ b/projects/RabbitMQ.Client/client/impl/RecoveryAwareChannel.cs @@ -62,15 +62,6 @@ protected override ulong AdjustDeliveryTag(ulong deliveryTag) return deliveryTag + ActiveDeliveryTagOffset; } - public override void BasicAck(ulong deliveryTag, bool multiple) - { - ulong realTag = deliveryTag - ActiveDeliveryTagOffset; - if (realTag > 0 && realTag <= deliveryTag) - { - base.BasicAck(realTag, multiple); - } - } - public override ValueTask BasicAckAsync(ulong deliveryTag, bool multiple) { ulong realTag = deliveryTag - ActiveDeliveryTagOffset; @@ -84,15 +75,6 @@ public override ValueTask BasicAckAsync(ulong deliveryTag, bool multiple) } } - public override void BasicNack(ulong deliveryTag, bool multiple, bool requeue) - { - ulong realTag = deliveryTag - ActiveDeliveryTagOffset; - if (realTag > 0 && realTag <= deliveryTag) - { - base.BasicNack(realTag, multiple, requeue); - } - } - public override ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue) { ulong realTag = deliveryTag - ActiveDeliveryTagOffset; @@ -106,16 +88,7 @@ public override ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool } } - public override void BasicReject(ulong deliveryTag, bool requeue) - { - ulong realTag = deliveryTag - ActiveDeliveryTagOffset; - if (realTag > 0 && realTag <= deliveryTag) - { - base.BasicReject(realTag, requeue); - } - } - - public override ValueTask BasicRejectAsync(ulong deliveryTag, bool requeue) + public override Task BasicRejectAsync(ulong deliveryTag, bool requeue) { ulong realTag = deliveryTag - ActiveDeliveryTagOffset; if (realTag > 0 && realTag <= deliveryTag) @@ -124,7 +97,7 @@ public override ValueTask BasicRejectAsync(ulong deliveryTag, bool requeue) } else { - return default; + return Task.CompletedTask; } } } diff --git a/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs b/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs index ef922b8c59..b0f9e766a9 100644 --- a/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs +++ b/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs @@ -248,7 +248,7 @@ await _closingSemaphore.WaitAsync() try { _channelWriter.Complete(); - if (_writerTask is not null) + if (_writerTask != null) { await _writerTask.ConfigureAwait(false); } diff --git a/projects/RabbitMQ.Client/client/logging/RabbitMqClientEventSource.cs b/projects/RabbitMQ.Client/client/logging/RabbitMqClientEventSource.cs index 6266b49aae..2729c9e35e 100644 --- a/projects/RabbitMQ.Client/client/logging/RabbitMqClientEventSource.cs +++ b/projects/RabbitMQ.Client/client/logging/RabbitMqClientEventSource.cs @@ -71,12 +71,6 @@ public void Error(string message, RabbitMqExceptionDetail ex) { #if NET6_0_OR_GREATER WriteExceptionEvent(message, ex); - - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The properties are preserved with the DynamicallyAccessedMembers attribute.")] - void WriteExceptionEvent<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string message, T ex) - { - WriteEvent(3, message, ex); - } #else WriteEvent(3, message, ex); #endif @@ -88,5 +82,13 @@ public void Error(string message, Exception ex) { Error(message, new RabbitMqExceptionDetail(ex)); } + +#if NET6_0_OR_GREATER + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The properties are preserved with the DynamicallyAccessedMembers attribute.")] + private void WriteExceptionEvent<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string message, T ex) + { + WriteEvent(3, message, ex); + } +#endif } } diff --git a/projects/Test/Applications/CreateChannel/Program.cs b/projects/Test/Applications/CreateChannel/Program.cs index 7365aca8b9..a490ae0032 100644 --- a/projects/Test/Applications/CreateChannel/Program.cs +++ b/projects/Test/Applications/CreateChannel/Program.cs @@ -15,24 +15,24 @@ public static class Program private static int channelsOpened; private static AutoResetEvent doneEvent; - public static void Main() + public static async Task Main() { ThreadPool.SetMinThreads(16 * Environment.ProcessorCount, 16 * Environment.ProcessorCount); doneEvent = new AutoResetEvent(false); var connectionFactory = new ConnectionFactory { DispatchConsumersAsync = true }; - var connection = connectionFactory.CreateConnection(); + IConnection connection = await connectionFactory.CreateConnectionAsync(); var watch = Stopwatch.StartNew(); - _ = Task.Run(() => + _ = Task.Run(async () => { var channels = new IChannel[ChannelsToOpen]; for (int i = 0; i < Repeats; i++) { for (int j = 0; j < channels.Length; j++) { - channels[j] = connection.CreateChannel(); + channels[j] = await connection.CreateChannelAsync(); channelsOpened++; } diff --git a/projects/Test/Applications/MassPublish/MassPublish.csproj b/projects/Test/Applications/MassPublish/MassPublish.csproj index b57323cb5a..63d513a986 100644 --- a/projects/Test/Applications/MassPublish/MassPublish.csproj +++ b/projects/Test/Applications/MassPublish/MassPublish.csproj @@ -9,8 +9,8 @@ - latest Exe + 9.0 diff --git a/projects/Test/Applications/MassPublish/Program.cs b/projects/Test/Applications/MassPublish/Program.cs index 78a31f00fd..a3938b69a8 100644 --- a/projects/Test/Applications/MassPublish/Program.cs +++ b/projects/Test/Applications/MassPublish/Program.cs @@ -76,7 +76,7 @@ static async Task Main() await consumeChannel.BasicQosAsync(prefetchSize: 0, prefetchCount: 128, global: false); await consumeChannel.ExchangeDeclareAsync(exchange: ExchangeName, - type: ExchangeType.Direct, passive: false, durable: false, autoDelete: false, arguments: null); + type: ExchangeType.Direct, passive: false, durable: false, autoDelete: false, noWait: false, arguments: null); await consumeChannel.QueueDeclareAsync(queue: QueueName, passive: false, durable: false, exclusive: false, autoDelete: false, arguments: null); @@ -125,6 +125,8 @@ static async Task Main() { Console.WriteLine("[DEBUG] channel {0} done publishing and waiting for confirms", publishChannel.ChannelNumber); } + + await publishChannel.CloseAsync(); })); } @@ -152,6 +154,8 @@ static async Task Main() await c.CloseAsync(); } + + await consumeChannel.CloseAsync(); } private static void PublishChannel_BasicNacks(object sender, BasicNackEventArgs e) diff --git a/projects/Test/AsyncIntegration/AsyncIntegration.csproj b/projects/Test/AsyncIntegration/AsyncIntegration.csproj index c7521605d9..c724726a05 100644 --- a/projects/Test/AsyncIntegration/AsyncIntegration.csproj +++ b/projects/Test/AsyncIntegration/AsyncIntegration.csproj @@ -11,9 +11,8 @@ ../../rabbit.snk true - latest - 7.0 true + 7.3 @@ -41,8 +40,8 @@ - - + + diff --git a/projects/Test/AsyncIntegration/AsyncIntegrationFixture.cs b/projects/Test/AsyncIntegration/AsyncIntegrationFixture.cs index 508b9b96ad..23398f6147 100644 --- a/projects/Test/AsyncIntegration/AsyncIntegrationFixture.cs +++ b/projects/Test/AsyncIntegration/AsyncIntegrationFixture.cs @@ -53,11 +53,6 @@ public class AsyncIntegrationFixture : IntegrationFixtureBase, IAsyncLifetime _openChannel = openChannel; } - protected override void SetUp() - { - // InitializeAsync - } - protected static Task AssertRanToCompletion(params Task[] tasks) { return DoAssertRanToCompletion(tasks); @@ -68,7 +63,7 @@ protected static Task AssertRanToCompletion(IEnumerable tasks) return DoAssertRanToCompletion(tasks); } - public virtual async Task InitializeAsync() + public override async Task InitializeAsync() { _connFactory = CreateConnectionFactory(); _connFactory.DispatchConsumersAsync = _dispatchConsumersAsync; @@ -92,33 +87,11 @@ public virtual async Task InitializeAsync() base.AddCallbackHandlers(); } - public virtual async Task DisposeAsync() - { - try - { - if (_channel != null) - { - await _channel.CloseAsync(); - } - await _conn.CloseAsync(); - } - finally - { - if (_channel != null) - { - _channel.Dispose(); - } - _conn.Dispose(); - _channel = null; - _conn = null; - } - } - private static async Task DoAssertRanToCompletion(IEnumerable tasks) { Task whenAllTask = Task.WhenAll(tasks); await whenAllTask; - Assert.Equal(TaskStatus.RanToCompletion, whenAllTask.Status); + Assert.True(whenAllTask.IsCompletedSuccessfully()); } } } diff --git a/projects/Test/AsyncIntegration/TestAsyncConsumer.cs b/projects/Test/AsyncIntegration/TestAsyncConsumer.cs index 3ce42591e2..49c77aedb5 100644 --- a/projects/Test/AsyncIntegration/TestAsyncConsumer.cs +++ b/projects/Test/AsyncIntegration/TestAsyncConsumer.cs @@ -173,22 +173,25 @@ public async Task TestBasicRoundtripConcurrentManyMessages() Task publishTask = Task.Run(async () => { - using (IChannel m = await _conn.CreateChannelAsync()) + using (IChannel publishChannel = await _conn.CreateChannelAsync()) { - QueueDeclareOk q = _channel.QueueDeclare(queue: queueName, exclusive: false, durable: true); + QueueDeclareOk pubQ = await publishChannel.QueueDeclareAsync(queue: queueName, exclusive: false, durable: true); + Assert.Equal(queueName, pubQ.QueueName); for (int i = 0; i < publish_total; i++) { - await _channel.BasicPublishAsync(string.Empty, queueName, body1); - await _channel.BasicPublishAsync(string.Empty, queueName, body2); + await publishChannel.BasicPublishAsync(string.Empty, queueName, body1); + await publishChannel.BasicPublishAsync(string.Empty, queueName, body2); } + + await publishChannel.CloseAsync(); } }); Task consumeTask = Task.Run(async () => { - using (IChannel m = await _conn.CreateChannelAsync()) + using (IChannel consumeChannel = await _conn.CreateChannelAsync()) { - var consumer = new AsyncEventingBasicConsumer(m); + var consumer = new AsyncEventingBasicConsumer(consumeChannel); int publish1_count = 0; int publish2_count = 0; @@ -214,7 +217,7 @@ public async Task TestBasicRoundtripConcurrentManyMessages() } }; - await _channel.BasicConsumeAsync(queueName, true, string.Empty, false, false, null, consumer); + await consumeChannel.BasicConsumeAsync(queueName, true, string.Empty, false, false, null, consumer); // ensure we get a delivery await AssertRanToCompletion(publish1SyncSource.Task, publish2SyncSource.Task); @@ -224,6 +227,8 @@ public async Task TestBasicRoundtripConcurrentManyMessages() bool result2 = await publish2SyncSource.Task; Assert.True(result2, $"Non concurrent dispatch lead to deadlock after {maximumWaitTime}"); + + await consumeChannel.CloseAsync(); } }); @@ -235,93 +240,95 @@ public async Task TestBasicRejectAsync() { var publishSyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - using var cancellationTokenSource = new CancellationTokenSource(TestTimeout); - - cancellationTokenSource.Token.Register(() => + using (var cancellationTokenSource = new CancellationTokenSource(TestTimeout)) { - publishSyncSource.SetCanceled(); - }); - - _conn.ConnectionShutdown += (o, ea) => - { - HandleConnectionShutdown(_conn, ea, (args) => + cancellationTokenSource.Token.Register(() => { - if (args.Initiator == ShutdownInitiator.Peer) - { - publishSyncSource.TrySetResult(false); - } + publishSyncSource.SetCanceled(); }); - }; - _channel.ChannelShutdown += (o, ea) => - { - HandleChannelShutdown(_channel, ea, (args) => + _conn.ConnectionShutdown += (o, ea) => { - if (args.Initiator == ShutdownInitiator.Peer) + HandleConnectionShutdown(_conn, ea, (args) => { - publishSyncSource.TrySetResult(false); - } - }); - }; - - var consumer = new AsyncEventingBasicConsumer(_channel); - consumer.Received += async (object sender, BasicDeliverEventArgs args) => - { - var c = sender as AsyncEventingBasicConsumer; - Assert.Same(c, consumer); - await _channel.BasicCancelAsync(c.ConsumerTags[0]); - /* - * https://github.com/rabbitmq/rabbitmq-dotnet-client/actions/runs/7450578332/attempts/1 - * That job failed with a bizarre error where the delivery tag ack timed out: - * - * AI.TestAsyncConsumer.TestBasicRejectAsync channel 1 shut down: - * AMQP close-reason, initiated by Peer, code=406, text= - * 'PRECONDITION_FAILED - delivery acknowledgement on channel 1 timed out. Timeout value used: 1800000 ms ...', classId=0, methodId=0 - * - * Added Task.Yield() to see if it ever happens again. - */ - await Task.Yield(); - await _channel.BasicRejectAsync(args.DeliveryTag, true); - publishSyncSource.TrySetResult(true); - }; - - QueueDeclareOk q = await _channel.QueueDeclareAsync(string.Empty, false, false, true, false, null); - string queueName = q.QueueName; - const string publish1 = "sync-hi-1"; - byte[] _body = _encoding.GetBytes(publish1); - await _channel.BasicPublishAsync(string.Empty, queueName, _body); + if (args.Initiator == ShutdownInitiator.Peer) + { + publishSyncSource.TrySetResult(false); + } + }); + }; - await _channel.BasicConsumeAsync(queue: queueName, autoAck: false, - consumerTag: string.Empty, noLocal: false, exclusive: false, - arguments: null, consumer); + _channel.ChannelShutdown += (o, ea) => + { + HandleChannelShutdown(_channel, ea, (args) => + { + if (args.Initiator == ShutdownInitiator.Peer) + { + publishSyncSource.TrySetResult(false); + } + }); + }; - Assert.True(await publishSyncSource.Task); + var consumer = new AsyncEventingBasicConsumer(_channel); + consumer.Received += async (object sender, BasicDeliverEventArgs args) => + { + var c = sender as AsyncEventingBasicConsumer; + Assert.Same(c, consumer); + await _channel.BasicCancelAsync(c.ConsumerTags[0]); + /* + * https://github.com/rabbitmq/rabbitmq-dotnet-client/actions/runs/7450578332/attempts/1 + * That job failed with a bizarre error where the delivery tag ack timed out: + * + * AI.TestAsyncConsumer.TestBasicRejectAsync channel 1 shut down: + * AMQP close-reason, initiated by Peer, code=406, text= + * 'PRECONDITION_FAILED - delivery acknowledgement on channel 1 timed out. Timeout value used: 1800000 ms ...', classId=0, methodId=0 + * + * Added Task.Yield() to see if it ever happens again. + */ + await Task.Yield(); + await _channel.BasicRejectAsync(args.DeliveryTag, true); + publishSyncSource.TrySetResult(true); + }; + + QueueDeclareOk q = await _channel.QueueDeclareAsync(queue: string.Empty, + durable: false, exclusive: true, autoDelete: false); + string queueName = q.QueueName; + const string publish1 = "sync-hi-1"; + byte[] _body = _encoding.GetBytes(publish1); + await _channel.BasicPublishAsync(string.Empty, queueName, _body); + + await _channel.BasicConsumeAsync(queue: queueName, autoAck: false, + consumerTag: string.Empty, noLocal: false, exclusive: false, + arguments: null, consumer); + + Assert.True(await publishSyncSource.Task); + + uint messageCount, consumerCount = 0; + ushort tries = 5; + do + { + QueueDeclareOk result = await _channel.QueueDeclarePassiveAsync(queue: queueName); + consumerCount = result.ConsumerCount; + messageCount = result.MessageCount; + if (consumerCount == 0 && messageCount > 0) + { + break; + } + else + { + await Task.Delay(500); + } + } while (tries-- > 0); - uint messageCount, consumerCount = 0; - ushort tries = 5; - do - { - QueueDeclareOk result = await _channel.QueueDeclareAsync(queue: queueName, passive: true, false, false, false, null); - consumerCount = result.ConsumerCount; - messageCount = result.MessageCount; - if (consumerCount == 0 && messageCount > 0) + if (tries == 0) { - break; + Assert.Fail("[ERROR] failed waiting for MessageCount > 0 && ConsumerCount == 0"); } else { - await Task.Delay(500); + Assert.Equal((uint)1, messageCount); + Assert.Equal((uint)0, consumerCount); } - } while (tries-- > 0); - - if (tries == 0) - { - Assert.Fail("[ERROR] failed waiting for MessageCount > 0 && ConsumerCount == 0"); - } - else - { - Assert.Equal((uint)1, messageCount); - Assert.Equal((uint)0, consumerCount); } } @@ -333,15 +340,9 @@ public async Task TestBasicAckAsync() var publishSyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var cf = CreateConnectionFactory(); - cf.DispatchConsumersAsync = true; - - using IConnection connection = await cf.CreateConnectionAsync(); - using IChannel channel = await connection.CreateChannelAsync(); - - connection.ConnectionShutdown += (o, ea) => + _conn.ConnectionShutdown += (o, ea) => { - HandleConnectionShutdown(connection, ea, (args) => + HandleConnectionShutdown(_conn, ea, (args) => { if (args.Initiator == ShutdownInitiator.Peer) { @@ -350,9 +351,9 @@ public async Task TestBasicAckAsync() }); }; - channel.ChannelShutdown += (o, ea) => + _channel.ChannelShutdown += (o, ea) => { - HandleChannelShutdown(channel, ea, (args) => + HandleChannelShutdown(_channel, ea, (args) => { if (args.Initiator == ShutdownInitiator.Peer) { @@ -361,14 +362,14 @@ public async Task TestBasicAckAsync() }); }; - await channel.ConfirmSelectAsync(); + await _channel.ConfirmSelectAsync(); - var consumer = new AsyncEventingBasicConsumer(channel); + var consumer = new AsyncEventingBasicConsumer(_channel); consumer.Received += async (object sender, BasicDeliverEventArgs args) => { var c = sender as AsyncEventingBasicConsumer; Assert.NotNull(c); - await channel.BasicAckAsync(args.DeliveryTag, false); + await _channel.BasicAckAsync(args.DeliveryTag, false); messagesReceived++; if (messagesReceived == messageCount) { @@ -376,11 +377,11 @@ public async Task TestBasicAckAsync() } }; - QueueDeclareOk q = await channel.QueueDeclareAsync(string.Empty, false, false, true, false, null); + QueueDeclareOk q = await _channel.QueueDeclareAsync(string.Empty, false, false, true); string queueName = q.QueueName; - await channel.BasicQosAsync(0, 1, false); - await channel.BasicConsumeAsync(queue: queueName, autoAck: false, + await _channel.BasicQosAsync(0, 1, false); + await _channel.BasicConsumeAsync(queue: queueName, autoAck: false, consumerTag: string.Empty, noLocal: false, exclusive: false, arguments: null, consumer); @@ -389,17 +390,16 @@ public async Task TestBasicAckAsync() for (int i = 0; i < messageCount; i++) { byte[] _body = _encoding.GetBytes(Guid.NewGuid().ToString()); - await channel.BasicPublishAsync(string.Empty, queueName, _body); + await _channel.BasicPublishAsync(string.Empty, queueName, _body); } }); - await channel.WaitForConfirmsOrDieAsync(); + await _channel.WaitForConfirmsOrDieAsync(); Assert.True(await publishSyncSource.Task); Assert.Equal(messageCount, messagesReceived); - // Note: closing channel explicitly just to test it. - await channel.CloseAsync(_closeArgs, false); + await _channel.CloseAsync(_closeArgs, false); } [Fact] @@ -407,15 +407,9 @@ public async Task TestBasicNackAsync() { var publishSyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var cf = CreateConnectionFactory(); - cf.DispatchConsumersAsync = true; - - using IConnection connection = await cf.CreateConnectionAsync(); - using IChannel channel = await connection.CreateChannelAsync(); - - connection.ConnectionShutdown += (o, ea) => + _conn.ConnectionShutdown += (o, ea) => { - HandleConnectionShutdown(connection, ea, (args) => + HandleConnectionShutdown(_conn, ea, (args) => { if (args.Initiator == ShutdownInitiator.Peer) { @@ -424,9 +418,9 @@ public async Task TestBasicNackAsync() }); }; - channel.ChannelShutdown += (o, ea) => + _channel.ChannelShutdown += (o, ea) => { - HandleChannelShutdown(channel, ea, (args) => + HandleChannelShutdown(_channel, ea, (args) => { if (args.Initiator == ShutdownInitiator.Peer) { @@ -435,23 +429,23 @@ public async Task TestBasicNackAsync() }); }; - var consumer = new AsyncEventingBasicConsumer(channel); + var consumer = new AsyncEventingBasicConsumer(_channel); consumer.Received += async (object sender, BasicDeliverEventArgs args) => { var c = sender as AsyncEventingBasicConsumer; Assert.NotNull(c); - await channel.BasicCancelAsync(c.ConsumerTags[0]); - await channel.BasicNackAsync(args.DeliveryTag, false, true); + await _channel.BasicCancelAsync(c.ConsumerTags[0]); + await _channel.BasicNackAsync(args.DeliveryTag, false, true); publishSyncSource.SetResult(true); }; - QueueDeclareOk q = await channel.QueueDeclareAsync(string.Empty, false, false, false, false, null); + QueueDeclareOk q = await _channel.QueueDeclareAsync(string.Empty, false, false, false); string queueName = q.QueueName; const string publish1 = "sync-hi-1"; byte[] _body = _encoding.GetBytes(publish1); - await channel.BasicPublishAsync(string.Empty, queueName, _body); + await _channel.BasicPublishAsync(string.Empty, queueName, _body); - await channel.BasicConsumeAsync(queue: queueName, autoAck: false, + await _channel.BasicConsumeAsync(queue: queueName, autoAck: false, consumerTag: string.Empty, noLocal: false, exclusive: false, arguments: null, consumer); @@ -461,7 +455,7 @@ public async Task TestBasicNackAsync() ushort tries = 5; do { - QueueDeclareOk result = await channel.QueueDeclareAsync(queue: queueName, passive: true, false, false, false, null); + QueueDeclareOk result = await _channel.QueueDeclarePassiveAsync(queue: queueName); consumerCount = result.ConsumerCount; messageCount = result.MessageCount; if (consumerCount == 0 && messageCount > 0) @@ -484,15 +478,14 @@ public async Task TestBasicNackAsync() Assert.Equal((uint)0, consumerCount); } - // Note: closing channel explicitly just to test it. - await channel.CloseAsync(_closeArgs, false); + await _channel.CloseAsync(_closeArgs, false); } [Fact] public async Task NonAsyncConsumerShouldThrowInvalidOperationException() { bool sawException = false; - QueueDeclareOk q = await _channel.QueueDeclareAsync(string.Empty, false, false, false, false, null); + QueueDeclareOk q = await _channel.QueueDeclareAsync(string.Empty, false, false, false); await _channel.BasicPublishAsync(string.Empty, q.QueueName, GetRandomBody(1024)); var consumer = new EventingBasicConsumer(_channel); try diff --git a/projects/Test/AsyncIntegration/TestAsyncConsumerExceptions.cs b/projects/Test/AsyncIntegration/TestAsyncConsumerExceptions.cs index d26b85fd68..dc1051d002 100644 --- a/projects/Test/AsyncIntegration/TestAsyncConsumerExceptions.cs +++ b/projects/Test/AsyncIntegration/TestAsyncConsumerExceptions.cs @@ -84,18 +84,18 @@ public Task TestDeliveryExceptionHandling() { IBasicConsumer consumer = new ConsumerFailingOnDelivery(_channel); return TestExceptionHandlingWith(consumer, (ch, q, c, ct) => - ch.BasicPublishAsync("", q, _encoding.GetBytes("msg"))); + ch.BasicPublishAsync("", q, _encoding.GetBytes("msg")).AsTask()); } protected async Task TestExceptionHandlingWith(IBasicConsumer consumer, - Func action) + Func action) { var waitSpan = TimeSpan.FromSeconds(5); var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var cts = new CancellationTokenSource(waitSpan); cts.Token.Register(() => tcs.TrySetResult(false)); - string q = await _channel.QueueDeclareAsync(string.Empty, false, false, true, false, null); + string q = await _channel.QueueDeclareAsync(string.Empty, false, true, false); _channel.CallbackException += (ch, evt) => { if (evt.Exception == TestException) diff --git a/projects/Test/AsyncIntegration/TestBasicGetAsync.cs b/projects/Test/AsyncIntegration/TestBasicGetAsync.cs index 6d43ebd0a3..48309660df 100644 --- a/projects/Test/AsyncIntegration/TestBasicGetAsync.cs +++ b/projects/Test/AsyncIntegration/TestBasicGetAsync.cs @@ -47,7 +47,7 @@ public async Task TestBasicGet() { const string msg = "for async basic.get"; - QueueDeclareOk queueResult = await _channel.QueueDeclareAsync(string.Empty, false, true, true, true, null); + QueueDeclareOk queueResult = await _channel.QueueDeclareAsync(string.Empty, false, true, true); string queueName = queueResult.QueueName; await _channel.BasicPublishAsync(string.Empty, queueName, _encoding.GetBytes(msg), true); @@ -55,7 +55,7 @@ public async Task TestBasicGet() BasicGetResult getResult = await _channel.BasicGetAsync(queueName, true); Assert.Equal(msg, _encoding.GetString(getResult.Body.ToArray())); - QueueDeclareOk queueResultPassive = await _channel.QueueDeclareAsync(queueName, true, true, true, true, null); + QueueDeclareOk queueResultPassive = await _channel.QueueDeclarePassiveAsync(queue: queueName); Assert.Equal((uint)0, queueResultPassive.MessageCount); Assert.Null(await _channel.BasicGetAsync(queueName, true)); diff --git a/projects/Test/AsyncIntegration/TestBasicPublishAsync.cs b/projects/Test/AsyncIntegration/TestBasicPublishAsync.cs index e6a526a130..6c58fb3f2f 100644 --- a/projects/Test/AsyncIntegration/TestBasicPublishAsync.cs +++ b/projects/Test/AsyncIntegration/TestBasicPublishAsync.cs @@ -51,7 +51,7 @@ public async Task TestQueuePurgeAsync() await _channel.ConfirmSelectAsync(); - QueueDeclareOk q = await _channel.QueueDeclareAsync(string.Empty, false, false, true, false, null); + QueueDeclareOk q = await _channel.QueueDeclareAsync(string.Empty, false, false, true); var publishTask = Task.Run(async () => { diff --git a/projects/Test/AsyncIntegration/TestConcurrentAccessWithSharedConnectionAsync.cs b/projects/Test/AsyncIntegration/TestConcurrentAccessWithSharedConnectionAsync.cs index 9626bf4fa8..32a16dc546 100644 --- a/projects/Test/AsyncIntegration/TestConcurrentAccessWithSharedConnectionAsync.cs +++ b/projects/Test/AsyncIntegration/TestConcurrentAccessWithSharedConnectionAsync.cs @@ -132,6 +132,7 @@ private Task TestConcurrentChannelOpenAndPublishingWithBodyAsync(byte[] body, in } Assert.True(await tcs.Task); + await ch.CloseAsync(); } }, iterations); } diff --git a/projects/Test/AsyncIntegration/TestConnectionFactory.cs b/projects/Test/AsyncIntegration/TestConnectionFactory.cs index 459e0ae0e8..15763a6d1d 100644 --- a/projects/Test/AsyncIntegration/TestConnectionFactory.cs +++ b/projects/Test/AsyncIntegration/TestConnectionFactory.cs @@ -45,53 +45,61 @@ public TestConnectionFactory(ITestOutputHelper output) : base(output) { } - protected override void SetUp() + public override Task InitializeAsync() { // NB: nothing to do here since each test creates its own factory, // connections and channels Assert.Null(_connFactory); Assert.Null(_conn); Assert.Null(_channel); + return Task.CompletedTask; } [Fact] public async Task TestCreateConnectionAsync_WithAlreadyCanceledToken() { - using var cts = new CancellationTokenSource(); - cts.Cancel(); + using (var cts = new CancellationTokenSource()) + { + cts.Cancel(); - ConnectionFactory cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); - bool passed = false; - /* - * If anyone wonders why TaskCanceledException is explicitly checked, - * even though it's a subclass of OperationCanceledException: - * https://github.com/rabbitmq/rabbitmq-dotnet-client/commit/383ca5c5f161edb717cf8fae7bf143c13143f634#r135400615 - */ - try - { - await cf.CreateConnectionAsync(cts.Token); - } - catch (TaskCanceledException) - { - passed = true; - } - catch (OperationCanceledException) - { - passed = true; - } + bool passed = false; + /* + * If anyone wonders why TaskCanceledException is explicitly checked, + * even though it's a subclass of OperationCanceledException: + * https://github.com/rabbitmq/rabbitmq-dotnet-client/commit/383ca5c5f161edb717cf8fae7bf143c13143f634#r135400615 + */ + try + { + await cf.CreateConnectionAsync(cts.Token); + } + catch (TaskCanceledException) + { + passed = true; + } + catch (OperationCanceledException) + { + passed = true; + } - Assert.True(passed, "FAIL did not see TaskCanceledException nor OperationCanceledException"); + Assert.True(passed, "FAIL did not see TaskCanceledException nor OperationCanceledException"); + } } [Fact] public async Task TestCreateConnectionAsync_UsesValidEndpointWhenMultipleSupplied() { - using var cts = new CancellationTokenSource(WaitSpan); - ConnectionFactory cf = CreateConnectionFactory(); - var invalidEp = new AmqpTcpEndpoint("not_localhost"); - var ep = new AmqpTcpEndpoint("localhost"); - using IConnection conn = await cf.CreateConnectionAsync(new List { invalidEp, ep }, cts.Token); + using (var cts = new CancellationTokenSource(WaitSpan)) + { + ConnectionFactory cf = CreateConnectionFactory(); + var invalidEp = new AmqpTcpEndpoint("not_localhost"); + var ep = new AmqpTcpEndpoint("localhost"); + using (IConnection conn = await cf.CreateConnectionAsync(new List { invalidEp, ep }, cts.Token)) + { + await conn.CloseAsync(); + } + } } } } diff --git a/projects/Test/AsyncIntegration/TestExchangeDeclareAsync.cs b/projects/Test/AsyncIntegration/TestExchangeDeclareAsync.cs index 6fee43ebf4..cb72ad6dbb 100644 --- a/projects/Test/AsyncIntegration/TestExchangeDeclareAsync.cs +++ b/projects/Test/AsyncIntegration/TestExchangeDeclareAsync.cs @@ -58,8 +58,8 @@ async Task f() { await Task.Delay(S_Random.Next(5, 50)); string exchangeName = GenerateExchangeName(); - await _channel.ExchangeDeclareAsync(exchange: exchangeName, type: "fanout", passive: false, false, false, null); - await _channel.ExchangeBindAsync(destination: "amq.fanout", source: exchangeName, routingKey: "unused", null); + await _channel.ExchangeDeclareAsync(exchange: exchangeName, type: "fanout", false, false); + await _channel.ExchangeBindAsync(destination: "amq.fanout", source: exchangeName, routingKey: "unused"); exchangeNames.Add(exchangeName); } catch (NotSupportedException e) @@ -82,7 +82,8 @@ async Task f() try { await Task.Delay(S_Random.Next(5, 50)); - await _channel.ExchangeUnbindAsync(destination: "amq.fanout", source: exchangeName, routingKey: "unused", null); + await _channel.ExchangeUnbindAsync(destination: "amq.fanout", source: exchangeName, routingKey: "unused", + noWait: false, arguments: null); await _channel.ExchangeDeleteAsync(exchange: exchangeName, ifUnused: false); } catch (NotSupportedException e) diff --git a/projects/Test/AsyncIntegration/TestExtensionsAsync.cs b/projects/Test/AsyncIntegration/TestExtensionsAsync.cs index 91cb8e9107..22a8ec1ff4 100644 --- a/projects/Test/AsyncIntegration/TestExtensionsAsync.cs +++ b/projects/Test/AsyncIntegration/TestExtensionsAsync.cs @@ -55,9 +55,9 @@ public async Task TestConfirmBarrier() } [Fact] - public Task TestConfirmBeforeWait() + public async Task TestConfirmBeforeWait() { - return Assert.ThrowsAsync(() => _channel.WaitForConfirmsAsync()); + await Assert.ThrowsAsync(() => _channel.WaitForConfirmsAsync()); } [Fact] @@ -65,18 +65,18 @@ public async Task TestExchangeBindingAsync() { await _channel.ConfirmSelectAsync(); - await _channel.ExchangeDeclareAsync("src", ExchangeType.Direct, false, false, false, null); - await _channel.ExchangeDeclareAsync("dest", ExchangeType.Direct, false, false, false, null); - string queue = await _channel.QueueDeclareAsync(string.Empty, false, false, true, false, null); + await _channel.ExchangeDeclareAsync("src", ExchangeType.Direct, false, false); + await _channel.ExchangeDeclareAsync("dest", ExchangeType.Direct, false, false); + string queue = await _channel.QueueDeclareAsync(string.Empty, false, false, true); - await _channel.ExchangeBindAsync("dest", "src", string.Empty, null); - await _channel.QueueBindAsync(queue, "dest", string.Empty, null); + await _channel.ExchangeBindAsync("dest", "src", string.Empty); + await _channel.QueueBindAsync(queue, "dest", string.Empty); await _channel.BasicPublishAsync("src", string.Empty); await _channel.WaitForConfirmsAsync(); Assert.NotNull(await _channel.BasicGetAsync(queue, true)); - await _channel.ExchangeUnbindAsync("dest", "src", string.Empty, null); + await _channel.ExchangeUnbindAsync("dest", "src", string.Empty); await _channel.BasicPublishAsync("src", string.Empty); await _channel.WaitForConfirmsAsync(); diff --git a/projects/Test/AsyncIntegration/TestFloodPublishingAsync.cs b/projects/Test/AsyncIntegration/TestFloodPublishingAsync.cs index ce845b5294..e196ea063a 100644 --- a/projects/Test/AsyncIntegration/TestFloodPublishingAsync.cs +++ b/projects/Test/AsyncIntegration/TestFloodPublishingAsync.cs @@ -185,6 +185,7 @@ public async Task TestMultithreadFloodPublishingAsync() } await pubCh.WaitForConfirmsOrDieAsync(); + await pubCh.CloseAsync(); } }); @@ -224,6 +225,7 @@ public async Task TestMultithreadFloodPublishingAsync() arguments: null, consumer: consumer); Assert.True(await tcs.Task); + await consumeCh.CloseAsync(); } await pub; diff --git a/projects/Test/AsyncIntegration/TestMessageCountAsync.cs b/projects/Test/AsyncIntegration/TestMessageCountAsync.cs index 55417d0732..e40dc79200 100644 --- a/projects/Test/AsyncIntegration/TestMessageCountAsync.cs +++ b/projects/Test/AsyncIntegration/TestMessageCountAsync.cs @@ -48,11 +48,11 @@ public async Task TestMessageCountMethod() await _channel.ConfirmSelectAsync(); string q = GenerateQueueName(); await _channel.QueueDeclareAsync(queue: q, passive: false, durable: false, exclusive: true, autoDelete: false, arguments: null); - Assert.Equal(0u, _channel.MessageCount(q)); + Assert.Equal(0u, await _channel.MessageCountAsync(q)); await _channel.BasicPublishAsync("", q, _encoding.GetBytes("msg")); await _channel.WaitForConfirmsAsync(); - Assert.Equal(1u, _channel.MessageCount(q)); + Assert.Equal(1u, await _channel.MessageCountAsync(q)); } } } diff --git a/projects/Test/AsyncIntegration/TestPassiveDeclareAsync.cs b/projects/Test/AsyncIntegration/TestPassiveDeclareAsync.cs index 54eb1fc032..43fd9a24e2 100644 --- a/projects/Test/AsyncIntegration/TestPassiveDeclareAsync.cs +++ b/projects/Test/AsyncIntegration/TestPassiveDeclareAsync.cs @@ -49,8 +49,8 @@ public Task TestPassiveExchangeDeclareWhenExchangeDoesNotExist() { return Assert.ThrowsAsync(() => { - ValueTask r = _channel.ExchangeDeclareAsync(Guid.NewGuid().ToString(), ExchangeType.Fanout, true, false, false, null); - return r.AsTask(); + return _channel.ExchangeDeclareAsync(exchange: Guid.NewGuid().ToString(), type: ExchangeType.Fanout, + passive: true, durable: true, autoDelete: false); }); } @@ -59,8 +59,8 @@ public Task TestPassiveQueueDeclareWhenQueueDoesNotExist() { return Assert.ThrowsAsync(() => { - ValueTask r = _channel.QueueDeclareAsync(Guid.NewGuid().ToString(), true, false, false, false, null); - return r.AsTask(); + return _channel.QueueDeclareAsync(queue: Guid.NewGuid().ToString(), passive: true, + durable: true, exclusive: true, autoDelete: false); }); } } diff --git a/projects/Test/AsyncIntegration/TestPublishSharedChannelAsync.cs b/projects/Test/AsyncIntegration/TestPublishSharedChannelAsync.cs index eb25891339..067a097800 100644 --- a/projects/Test/AsyncIntegration/TestPublishSharedChannelAsync.cs +++ b/projects/Test/AsyncIntegration/TestPublishSharedChannelAsync.cs @@ -75,30 +75,42 @@ public async Task MultiThreadPublishOnSharedChannel() using (IConnection conn = await cf.CreateConnectionAsync()) { - Assert.IsNotType(conn); - conn.ConnectionShutdown += HandleConnectionShutdown; - - using (IChannel channel = await conn.CreateChannelAsync()) + try { - channel.ChannelShutdown += HandleChannelShutdown; - await channel.ExchangeDeclareAsync(ExchangeName.Value, ExchangeType.Topic, passive: false, durable: false, autoDelete: true, arguments: null); - await channel.QueueDeclareAsync(QueueName, false, false, false, true, null); - await channel.QueueBindAsync(QueueName, ExchangeName.Value, PublishKey.Value, null); + Assert.IsNotType(conn); + conn.ConnectionShutdown += HandleConnectionShutdown; - try + using (IChannel channel = await conn.CreateChannelAsync()) { - for (int i = 0; i < Loops; i++) + try { - for (int j = 0; j < Repeats; j++) + channel.ChannelShutdown += HandleChannelShutdown; + await channel.ExchangeDeclareAsync(ExchangeName.Value, ExchangeType.Topic, passive: false, durable: false, autoDelete: true, + noWait: false, arguments: null); + await channel.QueueDeclareAsync(QueueName, exclusive: false, autoDelete: true); + await channel.QueueBindAsync(QueueName, ExchangeName.Value, PublishKey.Value); + + for (int i = 0; i < Loops; i++) { - await channel.BasicPublishAsync(ExchangeName, PublishKey, _body, false); + for (int j = 0; j < Repeats; j++) + { + await channel.BasicPublishAsync(ExchangeName, PublishKey, _body, false); + } } } + catch (Exception e) + { + _raisedException = e; + } + finally + { + await channel.CloseAsync(); + } } - catch (Exception e) - { - _raisedException = e; - } + } + finally + { + await conn.CloseAsync(); } } diff --git a/projects/Test/AsyncIntegration/TestPublisherConfirmsAsync.cs b/projects/Test/AsyncIntegration/TestPublisherConfirmsAsync.cs index 45cebdcb4c..8385a5ea7b 100644 --- a/projects/Test/AsyncIntegration/TestPublisherConfirmsAsync.cs +++ b/projects/Test/AsyncIntegration/TestPublisherConfirmsAsync.cs @@ -154,6 +154,7 @@ public async Task TestWaitForConfirmsWithEventsAsync() finally { await ch.QueueDeleteAsync(queue: queueName, ifUnused: false, ifEmpty: false); + await ch.CloseAsync(); } } } @@ -180,6 +181,7 @@ private async Task TestWaitForConfirmsAsync(int numberOfMessagesToPublish, Func< finally { await ch.QueueDeleteAsync(queue: queueName, ifUnused: false, ifEmpty: false); + await ch.CloseAsync(); } } } diff --git a/projects/Test/AsyncIntegration/TestQueueDeclareAsync.cs b/projects/Test/AsyncIntegration/TestQueueDeclareAsync.cs index f7ca9367d4..e8092e00ca 100644 --- a/projects/Test/AsyncIntegration/TestQueueDeclareAsync.cs +++ b/projects/Test/AsyncIntegration/TestQueueDeclareAsync.cs @@ -50,10 +50,10 @@ public async void TestQueueDeclare() { string q = GenerateQueueName(); - QueueDeclareOk declareResult = await _channel.QueueDeclareAsync(q, passive: false, false, false, false, null); + QueueDeclareOk declareResult = await _channel.QueueDeclareAsync(q, false, false, false); Assert.Equal(q, declareResult.QueueName); - QueueDeclareOk passiveDeclareResult = await _channel.QueueDeclareAsync(q, passive: true, false, false, false, null); + QueueDeclareOk passiveDeclareResult = await _channel.QueueDeclarePassiveAsync(q); Assert.Equal(q, passiveDeclareResult.QueueName); } @@ -97,9 +97,9 @@ async Task f() // sleep for a random amount of time to increase the chances // of thread interleaving. MK. await Task.Delay(S_Random.Next(5, 50)); - QueueDeclareOk r = await _channel.QueueDeclareAsync(queue: string.Empty, passive: false, false, false, false, null); + QueueDeclareOk r = await _channel.QueueDeclareAsync(queue: string.Empty, false, false, false); string queueName = r.QueueName; - await _channel.QueueBindAsync(queue: queueName, exchange: "amq.fanout", routingKey: queueName, null); + await _channel.QueueBindAsync(queue: queueName, exchange: "amq.fanout", routingKey: queueName); queues.Add(queueName); } catch (NotSupportedException e) @@ -125,7 +125,7 @@ async Task f() { await Task.Delay(S_Random.Next(5, 50)); - QueueDeclareOk r = await _channel.QueueDeclareAsync(qname, passive: true, false, false, false, null); + QueueDeclareOk r = await _channel.QueueDeclarePassiveAsync(qname); Assert.Equal(qname, r.QueueName); await _channel.QueueUnbindAsync(queue: qname, exchange: "amq.fanout", routingKey: qname, null); diff --git a/projects/Test/Common/Common.csproj b/projects/Test/Common/Common.csproj index 500a0b669d..39917eeea9 100644 --- a/projects/Test/Common/Common.csproj +++ b/projects/Test/Common/Common.csproj @@ -11,9 +11,8 @@ ../../rabbit.snk true - latest - 7.0 false + 7.3 @@ -27,7 +26,7 @@ - + diff --git a/projects/Test/Common/IntegrationFixtureBase.cs b/projects/Test/Common/IntegrationFixtureBase.cs index 7e3b172baa..53f25680e9 100644 --- a/projects/Test/Common/IntegrationFixtureBase.cs +++ b/projects/Test/Common/IntegrationFixtureBase.cs @@ -38,6 +38,7 @@ using System.Reflection; using System.Text; using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Exceptions; using RabbitMQ.Client.Framing.Impl; @@ -46,7 +47,7 @@ namespace Test { - public abstract class IntegrationFixtureBase : IDisposable + public abstract class IntegrationFixtureBase : IAsyncLifetime { private static bool s_isRunningInCI = false; private static bool s_isWindows = false; @@ -105,53 +106,64 @@ public IntegrationFixtureBase(ITestOutputHelper output) .Replace("AsyncIntegration.", "AI.") .Replace("Integration.", "I.") .Replace("SequentialI.", "SI."); - - SetUp(); } - protected virtual void SetUp() + public virtual async Task InitializeAsync() { + /* + * https://github.com/rabbitmq/rabbitmq-dotnet-client/commit/120f9bfce627f704956e1008d095b853b459d45b#r135400345 + * + * Integration tests must use CreateConnectionFactory so that ClientProvidedName is set for the connection. + * Tests that close connections via `rabbitmqctl` depend on finding the connection PID via its name. + */ if (_connFactory == null) { - /* - * https://github.com/rabbitmq/rabbitmq-dotnet-client/commit/120f9bfce627f704956e1008d095b853b459d45b#r135400345 - * - * Integration tests must use CreateConnectionFactory so that ClientProvidedName is set for the connection. - * Tests that close connections via `rabbitmqctl` depend on finding the connection PID via its name. - */ _connFactory = CreateConnectionFactory(); } if (_conn == null) { - _conn = CreateConnectionWithRetries(_connFactory); - _channel = _conn.CreateChannel(); + _conn = await CreateConnectionAsyncWithRetries(_connFactory); + _channel = await _conn.CreateChannelAsync(); AddCallbackHandlers(); } - } - public virtual void Dispose() - { - if (_channel != null) + if (_connFactory.AutomaticRecoveryEnabled) { - _channel.Dispose(); + Assert.IsType(_conn); } - - if (_conn != null) + else { - _conn.Dispose(); + Assert.IsType(_conn); } - - TearDown(); } - protected virtual void TearDown() + public virtual async Task DisposeAsync() { + try + { + if (_channel != null) + { + await _channel.CloseAsync(); + } + + if (_conn != null) + { + await _conn.CloseAsync(); + } + } + finally + { + _channel?.Dispose(); + _conn?.Dispose(); + _channel = null; + _conn = null; + } } protected virtual void AddCallbackHandlers() { - if (IntegrationFixtureBase.IsVerbose) + if (IsVerbose) { if (_conn != null) { @@ -206,13 +218,12 @@ protected static bool IsVerbose get { return s_isVerbose; } } - internal AutorecoveringConnection CreateAutorecoveringConnection(IEnumerable hostnames, bool expectException = false) + internal Task CreateAutorecoveringConnectionAsync(IEnumerable hostnames, bool expectException = false) { - - return CreateAutorecoveringConnection(hostnames, RequestedConnectionTimeout, RecoveryInterval, expectException); + return CreateAutorecoveringConnectionAsync(hostnames, RequestedConnectionTimeout, RecoveryInterval, expectException); } - internal AutorecoveringConnection CreateAutorecoveringConnection(IEnumerable hostnames, + internal async Task CreateAutorecoveringConnectionAsync(IEnumerable hostnames, TimeSpan requestedConnectionTimeout, TimeSpan networkRecoveryInterval, bool expectException = false) { if (hostnames is null) @@ -228,10 +239,11 @@ internal AutorecoveringConnection CreateAutorecoveringConnection(IEnumerable CreateConnectionAsyncWithRetries(ConnectionFactory connectionFactory, IEnumerable hostnames = null, bool expectException = false) { bool shouldRetry = IsWindows; @@ -243,11 +255,11 @@ internal AutorecoveringConnection CreateAutorecoveringConnection(IEnumerable ex is IOException).FirstOrDefault() as IOException; + ioex = agex1.InnerExceptions.Where(iex => iex is IOException).FirstOrDefault() as IOException; } - ioex ??= ex.InnerException as IOException; + if (ioex == null) + { + ioex = ex.InnerException as IOException; + } if (ioex is null) { @@ -277,7 +292,7 @@ internal AutorecoveringConnection CreateAutorecoveringConnection(IEnumerable action) + protected async Task WithTemporaryChannelAsync(Func action) { - IChannel channel = _conn.CreateChannel(); - + IChannel channel = await _conn.CreateChannelAsync(); try { - action(channel); + await action(channel); } finally { - channel.Abort(); + await channel.AbortAsync(); } } @@ -317,58 +331,69 @@ protected string GenerateQueueName() return $"{_testDisplayName}-queue-{Guid.NewGuid()}"; } - protected void WithTemporaryNonExclusiveQueue(Action action) + protected Task WithTemporaryNonExclusiveQueueAsync(Func action) { - WithTemporaryNonExclusiveQueue(_channel, action); + return WithTemporaryNonExclusiveQueueAsync(_channel, action); } - protected void WithTemporaryNonExclusiveQueue(IChannel channel, Action action) + protected Task WithTemporaryNonExclusiveQueueAsync(IChannel channel, Func action) { - WithTemporaryNonExclusiveQueue(channel, action, GenerateQueueName()); + return WithTemporaryNonExclusiveQueueAsync(channel, action, GenerateQueueName()); } - protected void WithTemporaryNonExclusiveQueue(IChannel channel, Action action, string queue) + protected async Task WithTemporaryNonExclusiveQueueAsync(IChannel channel, Func action, string queue) { try { - channel.QueueDeclare(queue, false, false, false, null); - action(channel, queue); + await channel.QueueDeclareAsync(queue, false, false, false); + await action(channel, queue); } finally { - WithTemporaryChannel(tm => tm.QueueDelete(queue)); + await WithTemporaryChannelAsync(ch => + { + return ch.QueueDeleteAsync(queue); + }); } } - protected void AssertMessageCount(string q, uint count) + protected Task AssertMessageCountAsync(string q, uint count) { - WithTemporaryChannel((m) => + return WithTemporaryChannelAsync(async ch => { - RabbitMQ.Client.QueueDeclareOk ok = m.QueueDeclarePassive(q); + RabbitMQ.Client.QueueDeclareOk ok = await ch.QueueDeclarePassiveAsync(q); Assert.Equal(count, ok.MessageCount); }); } - protected void AssertShutdownError(ShutdownEventArgs args, int code) + protected static void AssertShutdownError(ShutdownEventArgs args, int code) { Assert.Equal(args.ReplyCode, code); } - protected void AssertPreconditionFailed(ShutdownEventArgs args) + protected static void AssertPreconditionFailed(ShutdownEventArgs args) { AssertShutdownError(args, Constants.PreconditionFailed); } - protected void Wait(ManualResetEventSlim latch, string desc) + protected static Task WaitAsync(TaskCompletionSource tcs, string desc) { - Assert.True(latch.Wait(WaitSpan), - $"waiting {WaitSpan.TotalSeconds} seconds on a latch for '{desc}' timed out"); + return WaitAsync(tcs, WaitSpan, desc); } - protected void Wait(ManualResetEventSlim latch, TimeSpan timeSpan, string desc) + protected static async Task WaitAsync(TaskCompletionSource tcs, TimeSpan timeSpan, string desc) { - Assert.True(latch.Wait(timeSpan), - $"waiting {timeSpan.TotalSeconds} seconds on a latch for '{desc}' timed out"); + try + { + await tcs.Task.WaitAsync(timeSpan); + bool result = await tcs.Task; + Assert.True((true == result) && (tcs.Task.IsCompletedSuccessfully()), + $"waiting {timeSpan.TotalSeconds} seconds on a tcs for '{desc}' timed out"); + } + catch (TimeoutException) + { + Assert.Fail($"waiting {timeSpan.TotalSeconds} seconds on a tcs for '{desc}' timed out"); + } } protected ConnectionFactory CreateConnectionFactory() diff --git a/projects/Test/Common/ProcessUtil.cs b/projects/Test/Common/ProcessUtil.cs new file mode 100644 index 0000000000..7eb9196f68 --- /dev/null +++ b/projects/Test/Common/ProcessUtil.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading.Tasks; + +namespace Test +{ + /// + /// Process helper with asynchronous interface + /// https://gist.github.com/Indigo744/b5f3bd50df4b179651c876416bf70d0a + /// - Based on https://gist.github.com/georg-jung/3a8703946075d56423e418ea76212745 + /// - And on https://stackoverflow.com/questions/470256/process-waitforexit-asynchronously + /// + public static class ProcessUtil + { + /// + /// Run a process asynchronously + /// To capture STDOUT, set StartInfo.RedirectStandardOutput to TRUE + /// To capture STDERR, set StartInfo.RedirectStandardError to TRUE + /// + /// ProcessStartInfo object + /// The timeout in milliseconds (null for no timeout) + /// Result object + public static async Task RunAsync(ProcessStartInfo startInfo) + { + var result = new Result(); + + using (var process = new Process { StartInfo = startInfo, EnableRaisingEvents = true }) + { + // List of tasks to wait for a whole process exit + var processTasks = new List(); + + // === EXITED Event handling === + var processExitEvent = new TaskCompletionSource(); + + process.Exited += (sender, args) => + { + processExitEvent.TrySetResult(true); + }; + + processTasks.Add(processExitEvent.Task); + + // === STDOUT handling === + var stdOutBuilder = new StringBuilder(); + + if (process.StartInfo.RedirectStandardOutput) + { + var stdOutCloseEvent = new TaskCompletionSource(); + + process.OutputDataReceived += (s, e) => + { + if (e.Data == null) + { + stdOutCloseEvent.TrySetResult(true); + } + else + { + stdOutBuilder.AppendLine(e.Data); + } + }; + + processTasks.Add(stdOutCloseEvent.Task); + } + else + { + // STDOUT is not redirected, so we won't look for it + } + + // === STDERR handling === + var stdErrBuilder = new StringBuilder(); + + if (process.StartInfo.RedirectStandardError) + { + var stdErrCloseEvent = new TaskCompletionSource(); + + process.ErrorDataReceived += (s, e) => + { + if (e.Data == null) + { + stdErrCloseEvent.TrySetResult(true); + } + else + { + stdErrBuilder.AppendLine(e.Data); + } + }; + + processTasks.Add(stdErrCloseEvent.Task); + } + else + { + // STDERR is not redirected, so we won't look for it + } + + // === START OF PROCESS === + if (false == process.Start()) + { + result.ExitCode = process.ExitCode; + return result; + } + + // Reads the output stream first as needed and then waits because deadlocks are possible + if (process.StartInfo.RedirectStandardOutput) + { + process.BeginOutputReadLine(); + } + else + { + // No STDOUT + } + + if (process.StartInfo.RedirectStandardError) + { + process.BeginErrorReadLine(); + } + else + { + // No STDERR + } + + // === ASYNC WAIT OF PROCESS === + + // Process completion = exit AND stdout (if defined) AND stderr (if defined) + Task processCompletionTask = Task.WhenAll(processTasks); + + try + { + // Task to wait for exit OR timeout (if defined) + await processCompletionTask.WaitAsync(TimeSpan.FromSeconds(30)); + + // -> Process exited cleanly + result.ExitCode = process.ExitCode; + } + catch (OperationCanceledException) + { + // -> Timeout, let's kill the process + KillProcess(process); + throw; + } + catch (TimeoutException) + { + // -> Timeout, let's kill the process + KillProcess(process); + throw; + } + + // Read stdout/stderr + result.StdOut = stdOutBuilder.ToString(); + result.StdErr = stdErrBuilder.ToString(); + } + + return result; + } + + private static void KillProcess(Process process) + { + try + { + process.Kill(); + } + catch + { + // ignored + } + } + + /// + /// Run process result + /// + public class Result + { + /// + /// Exit code + /// If NULL, process exited due to timeout + /// + public int? ExitCode { get; set; } = null; + + /// + /// Standard error stream + /// + public string StdErr { get; set; } = ""; + + /// + /// Standard output stream + /// + public string StdOut { get; set; } = ""; + } + } +} diff --git a/projects/Test/Common/RabbitMQCtl.cs b/projects/Test/Common/RabbitMQCtl.cs index 4c57652a8f..994b92e9c1 100644 --- a/projects/Test/Common/RabbitMQCtl.cs +++ b/projects/Test/Common/RabbitMQCtl.cs @@ -33,6 +33,7 @@ using System.Diagnostics; using System.IO; using System.Text.RegularExpressions; +using System.Threading.Tasks; using RabbitMQ.Client; using Xunit.Abstractions; @@ -42,8 +43,10 @@ public class RabbitMQCtl { private static readonly char[] newLine = new char[] { '\n' }; private static readonly Func s_invokeRabbitMqCtl = GetRabbitMqCtlInvokeAction(); + // NOTE: \r? + // https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-options#multiline-mode private static readonly Regex s_getConnectionProperties = - new Regex(@"^(?<[^>]*>)\s\[.*""connection_name"",""(?[^""]*)"".*\]$", RegexOptions.Multiline | RegexOptions.Compiled); + new Regex(@"^(?<[^>]*>)\s\[.*""connection_name"",""(?[^""]*)"".*\]\r?$", RegexOptions.Multiline | RegexOptions.Compiled); private readonly ITestOutputHelper _output; @@ -52,47 +55,39 @@ public RabbitMQCtl(ITestOutputHelper output) _output = output; } - public void CloseConnection(IConnection conn) + public async Task CloseConnectionAsync(IConnection conn) { - CloseConnection(GetConnectionPid(conn.ClientProvidedName)); + string pid = await GetConnectionPidAsync(conn.ClientProvidedName); + await CloseConnectionAsync(pid); } - public void AddUser(string username, string password) + public Task AddUserAsync(string username, string password) { - ExecRabbitMQCtl($"add_user {username} {password}"); + return ExecRabbitMQCtlAsync($"add_user {username} {password}"); } - public void ChangePassword(string username, string password) + public Task ChangePasswordAsync(string username, string password) { - ExecRabbitMQCtl($"change_password {username} {password}"); + return ExecRabbitMQCtlAsync($"change_password {username} {password}"); } - public void SetPermissions(string username, string conf, string write, string read) + public Task SetPermissionsAsync(string username, string conf, string write, string read) { - ExecRabbitMQCtl($"set_permissions {username} \"{conf}\" \"{write}\" \"${read}\" "); + return ExecRabbitMQCtlAsync($"set_permissions {username} \"{conf}\" \"{write}\" \"${read}\" "); } - public void DeleteUser(string username) + public Task DeleteUserAsync(string username) { - ExecRabbitMQCtl($"delete_user {username}"); + return ExecRabbitMQCtlAsync($"delete_user {username}"); } - public string ExecRabbitMQCtl(string args) + public async Task ExecRabbitMQCtlAsync(string args) { try { - using var process = s_invokeRabbitMqCtl(args); - process.Start(); - process.WaitForExit(); - string stderr = process.StandardError.ReadToEnd(); - string stdout = process.StandardOutput.ReadToEnd(); - - if (stderr.Length > 0 || process.ExitCode > 0) - { - ReportExecFailure("rabbitmqctl", args, $"{stderr}\n{stdout}"); - } - - return stdout; + ProcessStartInfo rabbitmqCtlStartInfo = GetRabbitMqCtlStartInfo(args); + ProcessUtil.Result result = await ProcessUtil.RunAsync(rabbitmqCtlStartInfo); + return result.StdOut; } catch (Exception e) { @@ -106,6 +101,54 @@ private void ReportExecFailure(string cmd, string args, string msg) _output.WriteLine($"Failure while running {cmd} {args}:\n{msg}"); } + private static ProcessStartInfo GetRabbitMqCtlStartInfo(string args) + { + string envVariable = Environment.GetEnvironmentVariable("RABBITMQ_RABBITMQCTL_PATH"); + + if (false == string.IsNullOrWhiteSpace(envVariable)) + { + const string DockerPrefix = "DOCKER:"; + if (envVariable.StartsWith(DockerPrefix)) + { + // Call docker + return CreateProcessStartInfo("docker", + $"exec {envVariable.Substring(DockerPrefix.Length)} rabbitmqctl {args}"); + } + else + { + // call the path from the env var + return CreateProcessStartInfo(envVariable, args); + } + } + + // Try default + string umbrellaRabbitmqctlPath; + string providedRabbitmqctlPath; + + if (IsRunningOnMonoOrDotNetCore()) + { + umbrellaRabbitmqctlPath = "../../../../../../rabbit/scripts/rabbitmqctl"; + providedRabbitmqctlPath = "rabbitmqctl"; + } + else + { + umbrellaRabbitmqctlPath = @"..\..\..\..\..\..\rabbit\scripts\rabbitmqctl.bat"; + providedRabbitmqctlPath = "rabbitmqctl.bat"; + } + + string path = File.Exists(umbrellaRabbitmqctlPath) ? umbrellaRabbitmqctlPath : providedRabbitmqctlPath; + + if (IsRunningOnMonoOrDotNetCore()) + { + return CreateProcessStartInfo(path, args); + } + else + { + // TODO is cmd.exe really necessary? + return CreateProcessStartInfo("cmd.exe", $"/c \"\"{path}\" {args}"); + } + } + private static Func GetRabbitMqCtlInvokeAction() { string precomputedArguments; @@ -155,11 +198,11 @@ private void ReportExecFailure(string cmd, string args, string msg) } } - private string GetConnectionPid(string connectionName) + private async Task GetConnectionPidAsync(string connectionName) { - string stdout = ExecRabbitMQCtl("list_connections --silent pid client_properties"); + string stdout = await ExecRabbitMQCtlAsync("list_connections --silent pid client_properties"); - var match = s_getConnectionProperties.Match(stdout); + Match match = s_getConnectionProperties.Match(stdout); while (match.Success) { if (match.Groups["connection_name"].Value == connectionName) @@ -173,9 +216,9 @@ private string GetConnectionPid(string connectionName) throw new Exception($"No connection found with name: {connectionName}"); } - private void CloseConnection(string pid) + private Task CloseConnectionAsync(string pid) { - ExecRabbitMQCtl($"close_connection \"{pid}\" \"Closed via rabbitmqctl\""); + return ExecRabbitMQCtlAsync($"close_connection \"{pid}\" \"Closed via rabbitmqctl\""); } private static bool IsRunningOnMonoOrDotNetCore() @@ -187,6 +230,20 @@ private static bool IsRunningOnMonoOrDotNetCore() #endif } + private static ProcessStartInfo CreateProcessStartInfo(string cmd, string arguments, string workDirectory = null) + { + return new ProcessStartInfo + { + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true, + FileName = cmd, + Arguments = arguments, + WorkingDirectory = workDirectory + }; + } + private static Process CreateProcess(string cmd, string arguments, string workDirectory = null) { return new Process diff --git a/projects/Test/Common/TaskExtensions.cs b/projects/Test/Common/TaskExtensions.cs new file mode 100644 index 0000000000..f67222a448 --- /dev/null +++ b/projects/Test/Common/TaskExtensions.cs @@ -0,0 +1,105 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 2.0. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2020 VMware, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//--------------------------------------------------------------------------- +// +// The MPL v2.0: +// +//--------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2007-2020 VMware, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Test +{ + internal static class TaskExtensions + { +#if NET6_0_OR_GREATER + public static Task WaitAsync(this Task task, TimeSpan timeout) + { + if (task.IsCompletedSuccessfully) + { + return task; + } + + return task.WaitAsync(timeout); + } +#else + private static readonly TaskContinuationOptions s_tco = TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously; + private static void IgnoreTaskContinuation(Task t, object s) => t.Exception.Handle(e => true); + + public static Task WaitAsync(this Task task, TimeSpan timeout) + { + if (task.Status == TaskStatus.RanToCompletion) + { + return task; + } + + return DoTimeoutAfter(task, timeout); + } + + // https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#using-a-timeout + private static async Task DoTimeoutAfter(Task task, TimeSpan timeout) + { + using (var cts = new CancellationTokenSource()) + { + Task delayTask = Task.Delay(timeout, cts.Token); + Task resultTask = await Task.WhenAny(task, delayTask).ConfigureAwait(false); + if (resultTask == delayTask) + { + task.Ignore(); + throw new TimeoutException(); + } + else + { + cts.Cancel(); + } + + await task.ConfigureAwait(false); + } + } + + // https://github.com/dotnet/runtime/issues/23878 + // https://github.com/dotnet/runtime/issues/23878#issuecomment-1398958645 + private static void Ignore(this Task task) + { + if (task.IsCompleted) + { + _ = task.Exception; + } + else + { + _ = task.ContinueWith( + continuationAction: IgnoreTaskContinuation, + state: null, + cancellationToken: CancellationToken.None, + continuationOptions: s_tco, + scheduler: TaskScheduler.Default); + } + } +#endif + } +} diff --git a/projects/Test/Integration/Integration.csproj b/projects/Test/Integration/Integration.csproj index 47ac180a0e..1eca77ebcc 100644 --- a/projects/Test/Integration/Integration.csproj +++ b/projects/Test/Integration/Integration.csproj @@ -11,9 +11,8 @@ ../../rabbit.snk true - latest - 7.0 true + 7.3 @@ -37,8 +36,8 @@ - - + + diff --git a/projects/Test/Integration/TestAuth.cs b/projects/Test/Integration/TestAuth.cs index 790de61041..637c9490b3 100644 --- a/projects/Test/Integration/TestAuth.cs +++ b/projects/Test/Integration/TestAuth.cs @@ -29,6 +29,8 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- +using System.Threading.Tasks; +using RabbitMQ.Client; using RabbitMQ.Client.Exceptions; using Xunit; using Xunit.Abstractions; @@ -41,23 +43,26 @@ public TestAuth(ITestOutputHelper output) : base(output) { } - protected override void SetUp() + public override Task InitializeAsync() { + // NB: nothing to do here since each test creates its own factory, + // connections and channels Assert.Null(_connFactory); Assert.Null(_conn); Assert.Null(_channel); + return Task.CompletedTask; } [Fact] - public void TestAuthFailure() + public async Task TestAuthFailure() { - var connFactory = CreateConnectionFactory(); + ConnectionFactory connFactory = CreateConnectionFactory(); connFactory.UserName = "guest"; connFactory.Password = "incorrect-password"; try { - connFactory.CreateConnection(); + await connFactory.CreateConnectionAsync(); Assert.Fail("Exception caused by authentication failure expected"); } catch (BrokerUnreachableException bue) diff --git a/projects/Test/Integration/TestBasicGet.cs b/projects/Test/Integration/TestBasicGet.cs index 1bb486b9b9..254b42e547 100644 --- a/projects/Test/Integration/TestBasicGet.cs +++ b/projects/Test/Integration/TestBasicGet.cs @@ -30,6 +30,7 @@ //--------------------------------------------------------------------------- using System; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Exceptions; using Xunit; @@ -44,71 +45,77 @@ public TestBasicGet(ITestOutputHelper output) : base(output) } [Fact] - public void TestBasicGetWithClosedChannel() + public Task TestBasicGetWithClosedChannel() { - WithNonEmptyQueue((_, q) => - { - WithClosedChannel(cm => - { - Assert.Throws(() => cm.BasicGet(q, true)); - }); - }); + return WithNonEmptyQueueAsync((_, q) => + { + return WithClosedChannelAsync(ch => + { + return Assert.ThrowsAsync(() => + { + return ch.BasicGetAsync(q, true).AsTask(); + }); + }); + }); } [Fact] - public void TestBasicGetWithEmptyResponse() + public Task TestBasicGetWithEmptyResponse() { - WithEmptyQueue((channel, queue) => + return WithEmptyQueueAsync(async (channel, queue) => { - BasicGetResult res = channel.BasicGet(queue, false); + BasicGetResult res = await channel.BasicGetAsync(queue, false); Assert.Null(res); }); } [Fact] - public void TestBasicGetWithNonEmptyResponseAndAutoAckMode() + public Task TestBasicGetWithNonEmptyResponseAndAutoAckMode() { const string msg = "for basic.get"; - WithNonEmptyQueue((channel, queue) => + return WithNonEmptyQueueAsync(async (channel, queue) => { - BasicGetResult res = channel.BasicGet(queue, true); + BasicGetResult res = await channel.BasicGetAsync(queue, true); Assert.Equal(msg, _encoding.GetString(res.Body.ToArray())); - AssertMessageCount(queue, 0); + await AssertMessageCountAsync(queue, 0); }, msg); } - private void EnsureNotEmpty(string q, string body) + private Task EnsureNotEmptyAsync(string q, string body) { - WithTemporaryChannel(x => x.BasicPublish("", q, _encoding.GetBytes(body))); + return WithTemporaryChannelAsync(ch => + { + return ch.BasicPublishAsync("", q, _encoding.GetBytes(body)).AsTask(); + }); } - private void WithClosedChannel(Action action) + private async Task WithClosedChannelAsync(Func action) { - IChannel channel = _conn.CreateChannel(); - channel.Close(); - action(channel); + IChannel channel = await _conn.CreateChannelAsync(); + await channel.CloseAsync(); + await action(channel); } - private void WithNonEmptyQueue(Action action) + private Task WithNonEmptyQueueAsync(Func action) { - WithNonEmptyQueue(action, "msg"); + return WithNonEmptyQueueAsync(action, "msg"); } - private void WithNonEmptyQueue(Action action, string msg) + private Task WithNonEmptyQueueAsync(Func action, string msg) { - WithTemporaryNonExclusiveQueue((m, q) => + return WithTemporaryNonExclusiveQueueAsync(async (ch, q) => { - EnsureNotEmpty(q, msg); - action(m, q); + await EnsureNotEmptyAsync(q, msg); + await action(ch, q); }); } - private void WithEmptyQueue(Action action) + private Task WithEmptyQueueAsync(Func action) { - WithTemporaryNonExclusiveQueue((channel, queue) => + return WithTemporaryNonExclusiveQueueAsync(async (channel, queue) => { - channel.QueuePurge(queue); - action(channel, queue); + await channel.QueuePurgeAsync(queue); + await action(channel, queue); }); } } diff --git a/projects/Test/Integration/TestBasicPublish.cs b/projects/Test/Integration/TestBasicPublish.cs index 78287d61e0..7bab6cfb41 100644 --- a/projects/Test/Integration/TestBasicPublish.cs +++ b/projects/Test/Integration/TestBasicPublish.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; using Xunit; @@ -46,125 +47,134 @@ public TestBasicPublish(ITestOutputHelper output) : base(output) { } - protected override void SetUp() + public override Task InitializeAsync() { _connFactory = CreateConnectionFactory(); Assert.Null(_conn); Assert.Null(_channel); + return Task.CompletedTask; } [Fact] - public void TestBasicRoundtripArray() + public async Task TestBasicRoundtripArray() { - _conn = _connFactory.CreateConnection(); - _channel = _conn.CreateChannel(); + _conn = await _connFactory.CreateConnectionAsync(); + _channel = await _conn.CreateChannelAsync(); - QueueDeclareOk q = _channel.QueueDeclare(); + QueueDeclareOk q = await _channel.QueueDeclareAsync(); var bp = new BasicProperties(); byte[] sendBody = _encoding.GetBytes("hi"); byte[] consumeBody = null; var consumer = new EventingBasicConsumer(_channel); - var are = new AutoResetEvent(false); - consumer.Received += (o, a) => + using (var consumerReceivedSemaphore = new SemaphoreSlim(0, 1)) { - consumeBody = a.Body.ToArray(); - are.Set(); - }; - string tag = _channel.BasicConsume(q.QueueName, true, consumer); + consumer.Received += (o, a) => + { + consumeBody = a.Body.ToArray(); + consumerReceivedSemaphore.Release(); + }; + string tag = await _channel.BasicConsumeAsync(q.QueueName, true, consumer); - _channel.BasicPublish("", q.QueueName, bp, sendBody); - bool waitResFalse = are.WaitOne(5000); - _channel.BasicCancel(tag); + await _channel.BasicPublishAsync("", q.QueueName, bp, sendBody); + bool waitRes = await consumerReceivedSemaphore.WaitAsync(TimeSpan.FromSeconds(5)); + await _channel.BasicCancelAsync(tag); - Assert.True(waitResFalse); - Assert.Equal(sendBody, consumeBody); + Assert.True(waitRes); + Assert.Equal(sendBody, consumeBody); + } } [Fact] - public void TestBasicRoundtripCachedString() + public async Task TestBasicRoundtripCachedString() { - _conn = _connFactory.CreateConnection(); - _channel = _conn.CreateChannel(); + _conn = await _connFactory.CreateConnectionAsync(); + _channel = await _conn.CreateChannelAsync(); CachedString exchangeName = new CachedString(string.Empty); - CachedString queueName = new CachedString(_channel.QueueDeclare().QueueName); + CachedString queueName = new CachedString((await _channel.QueueDeclareAsync()).QueueName); byte[] sendBody = _encoding.GetBytes("hi"); byte[] consumeBody = null; var consumer = new EventingBasicConsumer(_channel); - var are = new AutoResetEvent(false); - consumer.Received += (o, a) => + using (var consumerReceivedSemaphore = new SemaphoreSlim(0, 1)) { - consumeBody = a.Body.ToArray(); - are.Set(); - }; - string tag = _channel.BasicConsume(queueName.Value, true, consumer); + consumer.Received += (o, a) => + { + consumeBody = a.Body.ToArray(); + consumerReceivedSemaphore.Release(); + }; + string tag = await _channel.BasicConsumeAsync(queueName.Value, true, consumer); - _channel.BasicPublish(exchangeName, queueName, sendBody); - bool waitResFalse = are.WaitOne(2000); - _channel.BasicCancel(tag); + await _channel.BasicPublishAsync(exchangeName, queueName, sendBody); + bool waitResFalse = await consumerReceivedSemaphore.WaitAsync(TimeSpan.FromSeconds(2)); + await _channel.BasicCancelAsync(tag); - Assert.True(waitResFalse); - Assert.Equal(sendBody, consumeBody); + Assert.True(waitResFalse); + Assert.Equal(sendBody, consumeBody); + } } [Fact] - public void TestBasicRoundtripReadOnlyMemory() + public async Task TestBasicRoundtripReadOnlyMemory() { - _conn = _connFactory.CreateConnection(); - _channel = _conn.CreateChannel(); + _conn = await _connFactory.CreateConnectionAsync(); + _channel = await _conn.CreateChannelAsync(); - QueueDeclareOk q = _channel.QueueDeclare(); + QueueDeclareOk q = await _channel.QueueDeclareAsync(); byte[] sendBody = _encoding.GetBytes("hi"); byte[] consumeBody = null; var consumer = new EventingBasicConsumer(_channel); - var are = new AutoResetEvent(false); - consumer.Received += (o, a) => + using (var consumerReceivedSemaphore = new SemaphoreSlim(0, 1)) { - consumeBody = a.Body.ToArray(); - are.Set(); - }; - string tag = _channel.BasicConsume(q.QueueName, true, consumer); + consumer.Received += (o, a) => + { + consumeBody = a.Body.ToArray(); + consumerReceivedSemaphore.Release(); + }; + string tag = await _channel.BasicConsumeAsync(q.QueueName, true, consumer); - _channel.BasicPublish("", q.QueueName, new ReadOnlyMemory(sendBody)); - bool waitResFalse = are.WaitOne(2000); - _channel.BasicCancel(tag); + await _channel.BasicPublishAsync("", q.QueueName, new ReadOnlyMemory(sendBody)); + bool waitRes = await consumerReceivedSemaphore.WaitAsync(TimeSpan.FromSeconds(2)); + await _channel.BasicCancelAsync(tag); - Assert.True(waitResFalse); - Assert.Equal(sendBody, consumeBody); + Assert.True(waitRes); + Assert.Equal(sendBody, consumeBody); + } } [Fact] - public void CanNotModifyPayloadAfterPublish() + public async Task CanNotModifyPayloadAfterPublish() { - _conn = _connFactory.CreateConnection(); - _channel = _conn.CreateChannel(); + _conn = await _connFactory.CreateConnectionAsync(); + _channel = await _conn.CreateChannelAsync(); - QueueDeclareOk q = _channel.QueueDeclare(); + QueueDeclareOk q = await _channel.QueueDeclareAsync(); byte[] sendBody = new byte[1000]; var consumer = new EventingBasicConsumer(_channel); - var receivedMessage = new AutoResetEvent(false); - bool modified = true; - consumer.Received += (o, a) => + using (var consumerReceivedSemaphore = new SemaphoreSlim(0, 1)) { - if (a.Body.Span.IndexOf((byte)1) < 0) + bool modified = true; + consumer.Received += (o, a) => { - modified = false; - } - receivedMessage.Set(); - }; - string tag = _channel.BasicConsume(q.QueueName, true, consumer); + if (a.Body.Span.IndexOf((byte)1) < 0) + { + modified = false; + } + consumerReceivedSemaphore.Release(); + }; + string tag = await _channel.BasicConsumeAsync(q.QueueName, true, consumer); - _channel.BasicPublish("", q.QueueName, sendBody); - sendBody.AsSpan().Fill(1); + await _channel.BasicPublishAsync("", q.QueueName, sendBody); + sendBody.AsSpan().Fill(1); - Assert.True(receivedMessage.WaitOne(5000)); - Assert.False(modified, "Payload was modified after the return of BasicPublish"); + Assert.True(await consumerReceivedSemaphore.WaitAsync(TimeSpan.FromSeconds(5))); + Assert.False(modified, "Payload was modified after the return of BasicPublish"); - _channel.BasicCancel(tag); + await _channel.BasicCancelAsync(tag); + } } [Fact] - public void TestMaxMessageSize() + public async Task TestMaxMessageSize() { var re = new ManualResetEventSlim(); const ushort maxMsgSize = 1024; @@ -183,7 +193,7 @@ public void TestMaxMessageSize() bool sawConsumerRegistered = false; bool sawConsumerCancelled = false; - using (IConnection c = cf.CreateConnection()) + using (IConnection c = await cf.CreateConnectionAsync()) { c.ConnectionShutdown += (o, a) => { @@ -194,7 +204,7 @@ public void TestMaxMessageSize() Assert.Equal(maxMsgSize, cf.Endpoint.MaxMessageSize); Assert.Equal(maxMsgSize, c.Endpoint.MaxMessageSize); - using (IChannel channel = c.CreateChannel()) + using (IChannel channel = await c.CreateChannelAsync()) { channel.ChannelShutdown += (o, a) => { @@ -206,7 +216,7 @@ public void TestMaxMessageSize() throw new XunitException("Unexpected channel.CallbackException"); }; - QueueDeclareOk q = channel.QueueDeclare(); + QueueDeclareOk q = await channel.QueueDeclareAsync(); var consumer = new EventingBasicConsumer(channel); @@ -235,10 +245,10 @@ public void TestMaxMessageSize() Interlocked.Increment(ref count); }; - string tag = channel.BasicConsume(q.QueueName, true, consumer); + string tag = await channel.BasicConsumeAsync(q.QueueName, true, consumer); - channel.BasicPublish("", q.QueueName, msg0); - channel.BasicPublish("", q.QueueName, msg1); + await channel.BasicPublishAsync("", q.QueueName, msg0); + await channel.BasicPublishAsync("", q.QueueName, msg1); Assert.True(re.Wait(TimeSpan.FromSeconds(5))); Assert.Equal(1, count); @@ -246,43 +256,47 @@ public void TestMaxMessageSize() Assert.True(sawChannelShutdown); Assert.True(sawConsumerRegistered); Assert.True(sawConsumerCancelled); + + await channel.CloseAsync(); } } } [Fact] - public void TestPropertiesRoundtrip_Headers() + public async Task TestPropertiesRoundtrip_Headers() { - _conn = _connFactory.CreateConnection(); - _channel = _conn.CreateChannel(); + _conn = await _connFactory.CreateConnectionAsync(); + _channel = await _conn.CreateChannelAsync(); var subject = new BasicProperties { Headers = new Dictionary() }; - QueueDeclareOk q = _channel.QueueDeclare(); + QueueDeclareOk q = await _channel.QueueDeclareAsync(); var bp = new BasicProperties() { Headers = new Dictionary() }; bp.Headers["Hello"] = "World"; byte[] sendBody = _encoding.GetBytes("hi"); byte[] consumeBody = null; var consumer = new EventingBasicConsumer(_channel); - var are = new AutoResetEvent(false); - string response = null; - consumer.Received += (o, a) => + using (var consumerReceivedSemaphore = new SemaphoreSlim(0, 1)) { - response = _encoding.GetString(a.BasicProperties.Headers["Hello"] as byte[]); - consumeBody = a.Body.ToArray(); - are.Set(); - }; + string response = null; + consumer.Received += (o, a) => + { + response = _encoding.GetString(a.BasicProperties.Headers["Hello"] as byte[]); + consumeBody = a.Body.ToArray(); + consumerReceivedSemaphore.Release(); + }; - string tag = _channel.BasicConsume(q.QueueName, true, consumer); - _channel.BasicPublish("", q.QueueName, bp, sendBody); - bool waitResFalse = are.WaitOne(5000); - _channel.BasicCancel(tag); - Assert.True(waitResFalse); - Assert.Equal(sendBody, consumeBody); - Assert.Equal("World", response); + string tag = await _channel.BasicConsumeAsync(q.QueueName, true, consumer); + await _channel.BasicPublishAsync("", q.QueueName, bp, sendBody); + bool waitResFalse = await consumerReceivedSemaphore.WaitAsync(TimeSpan.FromSeconds(5)); + await _channel.BasicCancelAsync(tag); + Assert.True(waitResFalse); + Assert.Equal(sendBody, consumeBody); + Assert.Equal("World", response); + } } } } diff --git a/projects/Test/Integration/TestChannelAllocation.cs b/projects/Test/Integration/TestChannelAllocation.cs index bdb3f71a37..a553ac1791 100644 --- a/projects/Test/Integration/TestChannelAllocation.cs +++ b/projects/Test/Integration/TestChannelAllocation.cs @@ -29,7 +29,6 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- -using System; using System.Collections.Generic; using System.Threading.Tasks; using RabbitMQ.Client; @@ -38,13 +37,12 @@ namespace Test.Integration { - public class TestChannelAllocation : IDisposable + public class TestChannelAllocation : IAsyncLifetime { - public const int CHANNEL_COUNT = 100; + private IConnection _c; + private const int CHANNEL_COUNT = 100; - IConnection _c; - - public TestChannelAllocation() + public async Task InitializeAsync() { var cf = new ConnectionFactory { @@ -52,70 +50,67 @@ public TestChannelAllocation() HandshakeContinuationTimeout = IntegrationFixture.WaitSpan, ClientProvidedName = nameof(TestChannelAllocation) }; - _c = cf.CreateConnection(); + + _c = await cf.CreateConnectionAsync(); } - public void Dispose() => _c.Close(); + public async Task DisposeAsync() + { + await _c.CloseAsync(); + } [Fact] - public void AllocateInOrder() + public async Task AllocateInOrder() { var channels = new List(); for (int i = 1; i <= CHANNEL_COUNT; i++) { - IChannel channel = _c.CreateChannel(); + IChannel channel = await _c.CreateChannelAsync(); channels.Add(channel); Assert.Equal(i, ChannelNumber(channel)); } foreach (IChannel channel in channels) { + await channel.CloseAsync(); channel.Dispose(); } } [Fact] - public void AllocateAfterFreeingLast() - { - using IChannel ch0 = _c.CreateChannel(); - Assert.Equal(1, ChannelNumber(ch0)); - ch0.Close(); - - using IChannel ch1 = _c.CreateChannel(); - Assert.Equal(1, ChannelNumber(ch1)); - } - - [Fact] - public async Task AllocateAfterFreeingLastAsync() + public async Task AllocateAfterFreeingLast() { - using IChannel ch0 = _c.CreateChannel(); + IChannel ch0 = await _c.CreateChannelAsync(); Assert.Equal(1, ChannelNumber(ch0)); await ch0.CloseAsync(); + ch0.Dispose(); - using IChannel ch1 = _c.CreateChannel(); + IChannel ch1 = await _c.CreateChannelAsync(); Assert.Equal(1, ChannelNumber(ch1)); + await ch1.CloseAsync(); + ch1.Dispose(); } [Fact] - public void AllocateAfterFreeingMany() + public async Task AllocateAfterFreeingMany() { var channels = new List(); for (int i = 1; i <= CHANNEL_COUNT; i++) { - channels.Add(_c.CreateChannel()); + channels.Add(await _c.CreateChannelAsync()); } foreach (IChannel channel in channels) { - channel.Close(); + await channel.CloseAsync(); } channels.Clear(); for (int j = 1; j <= CHANNEL_COUNT; j++) { - channels.Add(_c.CreateChannel()); + channels.Add(await _c.CreateChannelAsync()); } // In the current implementation the list should actually @@ -126,7 +121,7 @@ public void AllocateAfterFreeingMany() foreach (IChannel channel in channels) { Assert.Equal(k++, ChannelNumber(channel)); - channel.Close(); + await channel.CloseAsync(); } } diff --git a/projects/Test/Integration/TestChannelShutdown.cs b/projects/Test/Integration/TestChannelShutdown.cs index ceb4639b2b..8a190a1749 100644 --- a/projects/Test/Integration/TestChannelShutdown.cs +++ b/projects/Test/Integration/TestChannelShutdown.cs @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------- using System; -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Impl; using Xunit; @@ -45,20 +45,20 @@ public TestChannelShutdown(ITestOutputHelper output) : base(output) } [Fact] - public void TestConsumerDispatcherShutdown() + public async Task TestConsumerDispatcherShutdown() { - var m = (AutorecoveringChannel)_channel; - var latch = new ManualResetEventSlim(false); + var autorecoveringChannel = (AutorecoveringChannel)_channel; + var tcs = new TaskCompletionSource(); _channel.ChannelShutdown += (channel, args) => { - latch.Set(); + tcs.SetResult(true); }; - Assert.False(m.ConsumerDispatcher.IsShutdown, "dispatcher should NOT be shut down before Close"); - _channel.Close(); - Wait(latch, TimeSpan.FromSeconds(5), "channel shutdown"); - Assert.True(m.ConsumerDispatcher.IsShutdown, "dispatcher should be shut down after Close"); + Assert.False(autorecoveringChannel.ConsumerDispatcher.IsShutdown, "dispatcher should NOT be shut down before Close"); + await _channel.CloseAsync(); + await WaitAsync(tcs, TimeSpan.FromSeconds(5), "channel shutdown"); + Assert.True(autorecoveringChannel.ConsumerDispatcher.IsShutdown, "dispatcher should be shut down after Close"); } } } diff --git a/projects/Test/Integration/TestChannelSoftErrors.cs b/projects/Test/Integration/TestChannelSoftErrors.cs index b369c6a048..81fb062130 100644 --- a/projects/Test/Integration/TestChannelSoftErrors.cs +++ b/projects/Test/Integration/TestChannelSoftErrors.cs @@ -29,6 +29,7 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; using RabbitMQ.Client.Exceptions; @@ -44,29 +45,30 @@ public TestChannelSoftErrors(ITestOutputHelper output) : base(output) } [Fact] - public void TestBindOnNonExistingQueue() + public async Task TestBindOnNonExistingQueue() { - OperationInterruptedException exception = Assert.Throws(() => _channel.QueueBind("NonExistingQueue", "", string.Empty)); + OperationInterruptedException exception = await Assert.ThrowsAsync(() => _channel.QueueBindAsync("NonExistingQueue", "", string.Empty)); Assert.True(exception.Message.Contains("403"), $"Message doesn't contain the expected 403 part: {exception.Message}"); Assert.False(_channel.IsOpen, "Channel should be closed due to the soft error"); Assert.True(_conn.IsOpen, "Connection should still be open due to the soft error only closing the channel"); } [Fact] - public void TestBindOnNonExistingExchange() + public async Task TestBindOnNonExistingExchange() { - OperationInterruptedException exception = Assert.Throws(() => _channel.ExchangeBind("NonExistingQueue", "", string.Empty)); + OperationInterruptedException exception = await Assert.ThrowsAsync(() => _channel.ExchangeBindAsync("NonExistingQueue", "", string.Empty)); Assert.True(exception.Message.Contains("403"), $"Message doesn't contain the expected 403 part: {exception.Message}"); Assert.False(_channel.IsOpen, "Channel should be closed due to the soft error"); Assert.True(_conn.IsOpen, "Connection should still be open due to the soft error only closing the channel"); } [Fact] - public void TestConsumeOnNonExistingQueue() + public async Task TestConsumeOnNonExistingQueue() { - OperationInterruptedException exception = Assert.Throws(() => + OperationInterruptedException exception = await Assert.ThrowsAsync(() => { - var consumer = new EventingBasicConsumer(_channel); _channel.BasicConsume("NonExistingQueue", true, consumer); + var consumer = new EventingBasicConsumer(_channel); + return _channel.BasicConsumeAsync("NonExistingQueue", true, consumer); }); Assert.True(exception.Message.Contains("404"), $"Message doesn't contain the expected 404 part: {exception.Message}"); diff --git a/projects/Test/Integration/TestConcurrentAccessWithSharedConnection.cs b/projects/Test/Integration/TestConcurrentAccessWithSharedConnection.cs index c1384bf383..db2f0a1d3b 100644 --- a/projects/Test/Integration/TestConcurrentAccessWithSharedConnection.cs +++ b/projects/Test/Integration/TestConcurrentAccessWithSharedConnection.cs @@ -31,7 +31,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; @@ -42,119 +41,122 @@ namespace Test.Integration { public class TestConcurrentAccessWithSharedConnection : IntegrationFixture { - private const ushort _messageCount = 200; + private const ushort MessageCount = 256; - public TestConcurrentAccessWithSharedConnection(ITestOutputHelper output) : base(output) + public TestConcurrentAccessWithSharedConnection(ITestOutputHelper output) + : base(output) { } - protected override void SetUp() + public override async Task InitializeAsync() { _connFactory = CreateConnectionFactory(); - _conn = _connFactory.CreateConnection(); + _conn = await _connFactory.CreateConnectionAsync(); // NB: not creating _channel because this test suite doesn't use it. Assert.Null(_channel); } [Fact] - public void TestConcurrentChannelOpenAndPublishingWithBlankMessages() + public async Task TestConcurrentChannelOpenAndPublishingWithBlankMessages() { - TestConcurrentChannelOpenAndPublishingWithBody(Array.Empty(), 30); + await TestConcurrentChannelOpenAndPublishingWithBodyAsync(Array.Empty(), 30); } [Fact] - public void TestConcurrentChannelOpenAndPublishingSize64() + public async Task TestConcurrentChannelOpenAndPublishingSize64() { - TestConcurrentChannelOpenAndPublishingWithBodyOfSize(64); + await TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(64); } [Fact] - public void TestConcurrentChannelOpenAndPublishingSize256() + public async Task TestConcurrentChannelOpenAndPublishingSize256() { - TestConcurrentChannelOpenAndPublishingWithBodyOfSize(256); + await TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(256); } [Fact] - public void TestConcurrentChannelOpenAndPublishingSize1024() + public Task TestConcurrentChannelOpenAndPublishingSize1024() { - TestConcurrentChannelOpenAndPublishingWithBodyOfSize(1024); + return TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(1024); } [Fact] - public void TestConcurrentChannelOpenCloseLoop() + public async Task TestConcurrentChannelOpenCloseLoop() { - TestConcurrentChannelOperations((conn) => + await TestConcurrentChannelOperationsAsync(async (conn) => { - using (IChannel ch = conn.CreateChannel()) + using (IChannel ch = await conn.CreateChannelAsync()) { - ch.Close(); + await ch.CloseAsync(); } }, 50); } - private void TestConcurrentChannelOpenAndPublishingWithBodyOfSize(ushort length, int iterations = 30) + private Task TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(ushort length, int iterations = 30) { byte[] body = GetRandomBody(length); - TestConcurrentChannelOpenAndPublishingWithBody(body, iterations); + return TestConcurrentChannelOpenAndPublishingWithBodyAsync(body, iterations); } - private void TestConcurrentChannelOpenAndPublishingWithBody(byte[] body, int iterations) + private Task TestConcurrentChannelOpenAndPublishingWithBodyAsync(byte[] body, int iterations) { - TestConcurrentChannelOperations((conn) => + return TestConcurrentChannelOperationsAsync(async (conn) => { - using (var localLatch = new ManualResetEvent(false)) - { - // publishing on a shared channel is not supported - // and would missing the point of this test anyway - using (IChannel ch = _conn.CreateChannel()) - { - ch.ConfirmSelect(); - - ch.BasicAcks += (object sender, BasicAckEventArgs e) => - { - if (e.DeliveryTag >= _messageCount) - { - localLatch.Set(); - } - }; + var tcs = new TaskCompletionSource(); - ch.BasicNacks += (object sender, BasicNackEventArgs e) => - { - localLatch.Set(); - Assert.Fail("should never see a nack"); - }; + // publishing on a shared channel is not supported + // and would missing the point of this test anyway + using (IChannel ch = await _conn.CreateChannelAsync()) + { + await ch.ConfirmSelectAsync(); - QueueDeclareOk q = ch.QueueDeclare(queue: string.Empty, exclusive: true, autoDelete: true); - for (ushort j = 0; j < _messageCount; j++) + ch.BasicAcks += (object sender, BasicAckEventArgs e) => + { + if (e.DeliveryTag >= MessageCount) { - ch.BasicPublish("", q.QueueName, body, true); + tcs.SetResult(true); } + }; + + ch.BasicNacks += (object sender, BasicNackEventArgs e) => + { + tcs.SetResult(false); + Assert.Fail("should never see a nack"); + }; - Assert.True(localLatch.WaitOne(WaitSpan)); + QueueDeclareOk q = await ch.QueueDeclareAsync(queue: string.Empty, exclusive: true, autoDelete: true); + for (ushort j = 0; j < MessageCount; j++) + { + await ch.BasicPublishAsync("", q.QueueName, body, true); } + + Assert.True(await tcs.Task); + + // NOTE: this is very important before a Dispose(); + await ch.CloseAsync(); } }, iterations); } - private void TestConcurrentChannelOperations(Action actions, int iterations) - { - TestConcurrentChannelOperations(actions, iterations, LongWaitSpan); - } - - private void TestConcurrentChannelOperations(Action action, int iterations, TimeSpan timeout) + private async Task TestConcurrentChannelOperationsAsync(Func action, int iterations) { var tasks = new List(); for (int i = 0; i < _processorCount; i++) { - tasks.Add(Task.Run(() => + tasks.Add(Task.Run(async () => { for (int j = 0; j < iterations; j++) { - action(_conn); + await action(_conn); } })); } - Assert.True(Task.WaitAll(tasks.ToArray(), timeout)); + + Task whenTask = Task.WhenAll(tasks); + await whenTask.WaitAsync(LongWaitSpan); + Assert.True(whenTask.IsCompleted); + Assert.False(whenTask.IsCanceled); + Assert.False(whenTask.IsFaulted); // incorrect frame interleaving in these tests will result // in an unrecoverable connection-level exception, thus diff --git a/projects/Test/Integration/TestConfirmSelect.cs b/projects/Test/Integration/TestConfirmSelect.cs index cf2374805d..4adaf31d06 100644 --- a/projects/Test/Integration/TestConfirmSelect.cs +++ b/projects/Test/Integration/TestConfirmSelect.cs @@ -29,6 +29,7 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- +using System.Threading.Tasks; using RabbitMQ.Client; using Xunit; using Xunit.Abstractions; @@ -42,44 +43,44 @@ public TestConfirmSelect(ITestOutputHelper output) : base(output) } [Fact] - public void TestConfirmSelectIdempotency() + public async Task TestConfirmSelectIdempotency() { - void Publish() + ValueTask PublishAsync() { - _channel.BasicPublish("", "amq.fanout", _encoding.GetBytes("message")); + return _channel.BasicPublishAsync("", "amq.fanout", _encoding.GetBytes("message")); } - _channel.ConfirmSelect(); + await _channel.ConfirmSelectAsync(); Assert.Equal(1ul, _channel.NextPublishSeqNo); - Publish(); + await PublishAsync(); Assert.Equal(2ul, _channel.NextPublishSeqNo); - Publish(); + await PublishAsync(); Assert.Equal(3ul, _channel.NextPublishSeqNo); - _channel.ConfirmSelect(); - Publish(); + await _channel.ConfirmSelectAsync(); + await PublishAsync(); Assert.Equal(4ul, _channel.NextPublishSeqNo); - Publish(); + await PublishAsync(); Assert.Equal(5ul, _channel.NextPublishSeqNo); - Publish(); + await PublishAsync(); Assert.Equal(6ul, _channel.NextPublishSeqNo); } [Theory] [InlineData(255)] [InlineData(256)] - public void TestDeliveryTagDiverged_GH1043(ushort correlationIdLength) + public async Task TestDeliveryTagDiverged_GH1043(ushort correlationIdLength) { byte[] body = GetRandomBody(16); - _channel.ExchangeDeclare("sample", "fanout", autoDelete: true); + await _channel.ExchangeDeclareAsync("sample", "fanout", autoDelete: true); // _channel.BasicAcks += (s, e) => _output.WriteLine("Acked {0}", e.DeliveryTag); - _channel.ConfirmSelect(); + await _channel.ConfirmSelectAsync(); var properties = new BasicProperties(); // _output.WriteLine("Client delivery tag {0}", _channel.NextPublishSeqNo); - _channel.BasicPublish(exchange: "sample", routingKey: string.Empty, in properties, body); - _channel.WaitForConfirmsOrDie(); + await _channel.BasicPublishAsync(exchange: "sample", routingKey: string.Empty, in properties, body); + await _channel.WaitForConfirmsOrDieAsync(); try { @@ -88,8 +89,8 @@ public void TestDeliveryTagDiverged_GH1043(ushort correlationIdLength) CorrelationId = new string('o', correlationIdLength) }; // _output.WriteLine("Client delivery tag {0}", _channel.NextPublishSeqNo); - _channel.BasicPublish("sample", string.Empty, in properties, body); - _channel.WaitForConfirmsOrDie(); + await _channel.BasicPublishAsync("sample", string.Empty, in properties, body); + await _channel.WaitForConfirmsOrDieAsync(); } catch { @@ -98,8 +99,8 @@ public void TestDeliveryTagDiverged_GH1043(ushort correlationIdLength) properties = new BasicProperties(); // _output.WriteLine("Client delivery tag {0}", _channel.NextPublishSeqNo); - _channel.BasicPublish("sample", string.Empty, in properties, body); - _channel.WaitForConfirmsOrDie(); + await _channel.BasicPublishAsync("sample", string.Empty, in properties, body); + await _channel.WaitForConfirmsOrDieAsync(); // _output.WriteLine("I'm done..."); } } diff --git a/projects/Test/Integration/TestConnectionFactory.cs b/projects/Test/Integration/TestConnectionFactory.cs index 8362a78c95..71d4ca0d3a 100644 --- a/projects/Test/Integration/TestConnectionFactory.cs +++ b/projects/Test/Integration/TestConnectionFactory.cs @@ -31,6 +31,7 @@ using System.Collections.Generic; using System.Net.Sockets; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Exceptions; using Xunit; @@ -44,13 +45,14 @@ public TestConnectionFactory(ITestOutputHelper output) : base(output) { } - protected override void SetUp() + public override Task InitializeAsync() { // NB: nothing to do here since each test creates its own factory, // connections and channels Assert.Null(_connFactory); Assert.Null(_conn); Assert.Null(_channel); + return Task.CompletedTask; } [Fact] @@ -97,7 +99,7 @@ public void TestConnectionFactoryWithCustomSocketFactory() defaultSendBufsz = defaultSocket.SendBufferSize; } - ConnectionFactory cf = new() + var cf = new ConnectionFactory { SocketFactory = (AddressFamily af) => { @@ -121,246 +123,271 @@ public void TestConnectionFactoryWithCustomSocketFactory() } [Fact] - public void TestCreateConnectionUsesSpecifiedPort() + public async Task TestCreateConnectionUsesSpecifiedPort() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; cf.HostName = "localhost"; cf.Port = 1234; - Assert.Throws(() => + await Assert.ThrowsAsync(() => { - using IConnection conn = cf.CreateConnection(); + return cf.CreateConnectionAsync(); }); } [Fact] - public void TestCreateConnectionWithClientProvidedNameUsesSpecifiedPort() + public async Task TestCreateConnectionWithClientProvidedNameUsesSpecifiedPort() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; cf.HostName = "localhost"; cf.Port = 123; - Assert.Throws(() => + await Assert.ThrowsAsync(() => { - using IConnection conn = cf.CreateConnection(); + return cf.CreateConnectionAsync(); }); } [Fact] - public void TestCreateConnectionWithClientProvidedNameUsesDefaultName() + public async Task TestCreateConnectionWithClientProvidedNameUsesDefaultName() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = false; string expectedName = cf.ClientProvidedName; - using (IConnection conn = cf.CreateConnection()) + using (IConnection conn = await cf.CreateConnectionAsync()) { Assert.Equal(expectedName, conn.ClientProvidedName); Assert.Equal(expectedName, conn.ClientProperties["connection_name"]); + await conn.CloseAsync(); } } [Fact] - public void TestCreateConnectionWithClientProvidedNameUsesNameArgumentValue() + public async Task TestCreateConnectionWithClientProvidedNameUsesNameArgumentValue() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = false; string expectedName = cf.ClientProvidedName; - using (IConnection conn = cf.CreateConnection(expectedName)) + using (IConnection conn = await cf.CreateConnectionAsync(expectedName)) { Assert.Equal(expectedName, conn.ClientProvidedName); Assert.Equal(expectedName, conn.ClientProperties["connection_name"]); + await conn.CloseAsync(); } } [Fact] - public void TestCreateConnectionWithClientProvidedNameAndAutorecoveryUsesNameArgumentValue() + public async Task TestCreateConnectionWithClientProvidedNameAndAutorecoveryUsesNameArgumentValue() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; string expectedName = cf.ClientProvidedName; - using (IConnection conn = cf.CreateConnection(expectedName)) + using (IConnection conn = await cf.CreateConnectionAsync(expectedName)) { Assert.Equal(expectedName, conn.ClientProvidedName); Assert.Equal(expectedName, conn.ClientProperties["connection_name"]); + await conn.CloseAsync(); } } [Fact] - public void TestCreateConnectionAmqpTcpEndpointListAndClientProvidedName() + public async Task TestCreateConnectionAmqpTcpEndpointListAndClientProvidedName() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; string expectedName = cf.ClientProvidedName; var xs = new List { new AmqpTcpEndpoint("localhost") }; - using (IConnection conn = cf.CreateConnection(xs, expectedName)) + using (IConnection conn = await cf.CreateConnectionAsync(xs, expectedName)) { Assert.Equal(expectedName, conn.ClientProvidedName); Assert.Equal(expectedName, conn.ClientProperties["connection_name"]); + await conn.CloseAsync(); } } [Fact] - public void TestCreateConnectionUsesDefaultPort() + public async Task TestCreateConnectionUsesDefaultPort() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; cf.HostName = "localhost"; - using (IConnection conn = cf.CreateConnection()) + using (IConnection conn = await cf.CreateConnectionAsync()) { Assert.Equal(5672, conn.Endpoint.Port); + await conn.CloseAsync(); } } [Fact] - public void TestCreateConnectionUsesDefaultMaxMessageSize() + public async Task TestCreateConnectionUsesDefaultMaxMessageSize() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; cf.HostName = "localhost"; Assert.Equal(ConnectionFactory.DefaultMaxMessageSize, cf.MaxMessageSize); Assert.Equal(ConnectionFactory.DefaultMaxMessageSize, cf.Endpoint.MaxMessageSize); - using (IConnection conn = cf.CreateConnection()) + using (IConnection conn = await cf.CreateConnectionAsync()) { Assert.Equal(ConnectionFactory.DefaultMaxMessageSize, conn.Endpoint.MaxMessageSize); + await conn.CloseAsync(); } } [Fact] - public void TestCreateConnectionWithoutAutoRecoverySelectsAHostFromTheList() + public async Task TestCreateConnectionWithoutAutoRecoverySelectsAHostFromTheList() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = false; cf.HostName = "not_localhost"; - IConnection conn = cf.CreateConnection(new List { "localhost" }); - conn.Close(); + IConnection conn = await cf.CreateConnectionAsync(new List { "localhost" }); + await conn.CloseAsync(); conn.Dispose(); Assert.Equal("not_localhost", cf.HostName); Assert.Equal("localhost", conn.Endpoint.HostName); } [Fact] - public void TestCreateConnectionWithAutoRecoveryUsesAmqpTcpEndpoint() + public async Task TestCreateConnectionWithAutoRecoveryUsesAmqpTcpEndpoint() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; cf.HostName = "not_localhost"; cf.Port = 1234; var ep = new AmqpTcpEndpoint("localhost"); - using (IConnection conn = cf.CreateConnection(new List { ep })) { } + using (IConnection conn = await cf.CreateConnectionAsync(new List { ep })) + { + await conn.CloseAsync(); + } } [Fact] - public void TestCreateConnectionWithAutoRecoveryUsesInvalidAmqpTcpEndpoint() + public async Task TestCreateConnectionWithAutoRecoveryUsesInvalidAmqpTcpEndpoint() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; var ep = new AmqpTcpEndpoint("localhost", 1234); - Assert.Throws(() => { using IConnection conn = cf.CreateConnection(new List { ep }); }); + await Assert.ThrowsAsync(() => + { + return cf.CreateConnectionAsync(new List { ep }); + }); } [Fact] - public void TestCreateConnectionUsesAmqpTcpEndpoint() + public async Task TestCreateConnectionUsesAmqpTcpEndpoint() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.HostName = "not_localhost"; cf.Port = 1234; var ep = new AmqpTcpEndpoint("localhost"); - using (IConnection conn = cf.CreateConnection(new List { ep })) { } + using (IConnection conn = await cf.CreateConnectionAsync(new List { ep })) + { + await conn.CloseAsync(); + } } [Fact] - public void TestCreateConnectionWithForcedAddressFamily() + public async Task TestCreateConnectionWithForcedAddressFamily() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.HostName = "not_localhost"; var ep = new AmqpTcpEndpoint("localhost") { AddressFamily = System.Net.Sockets.AddressFamily.InterNetwork }; cf.Endpoint = ep; - using IConnection conn = cf.CreateConnection(); + using (IConnection conn = await cf.CreateConnectionAsync()) + { + await conn.CloseAsync(); + } } [Fact] - public void TestCreateConnectionUsesInvalidAmqpTcpEndpoint() + public async Task TestCreateConnectionUsesInvalidAmqpTcpEndpoint() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); var ep = new AmqpTcpEndpoint("localhost", 1234); - Assert.Throws(() => + await Assert.ThrowsAsync(() => { - using IConnection conn = cf.CreateConnection(new List { ep }); + return cf.CreateConnectionAsync(new List { ep }); }); } [Fact] - public void TestCreateConnectionUsesValidEndpointWhenMultipleSupplied() + public async Task TestCreateConnectionUsesValidEndpointWhenMultipleSupplied() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); var invalidEp = new AmqpTcpEndpoint("not_localhost"); var ep = new AmqpTcpEndpoint("localhost"); - using IConnection conn = cf.CreateConnection(new List { invalidEp, ep }); + using (IConnection conn = await cf.CreateConnectionAsync(new List { invalidEp, ep })) + { + await conn.CloseAsync(); + } } [Fact] public void TestCreateAmqpTCPEndPointOverridesMaxMessageSizeWhenGreaterThanMaximumAllowed() { - var ep = new AmqpTcpEndpoint("localhost", -1, new SslOption(), ConnectionFactory.MaximumMaxMessageSize); + _ = new AmqpTcpEndpoint("localhost", -1, new SslOption(), ConnectionFactory.MaximumMaxMessageSize); } [Fact] - public void TestCreateConnectionUsesConfiguredMaxMessageSize() + public async Task TestCreateConnectionUsesConfiguredMaxMessageSize() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.MaxMessageSize = 1500; - using (IConnection conn = cf.CreateConnection()) + using (IConnection conn = await cf.CreateConnectionAsync()) { Assert.Equal(cf.MaxMessageSize, conn.Endpoint.MaxMessageSize); - }; + await conn.CloseAsync(); + } } [Fact] - public void TestCreateConnectionWithAmqpEndpointListUsesAmqpTcpEndpointMaxMessageSize() + public async Task TestCreateConnectionWithAmqpEndpointListUsesAmqpTcpEndpointMaxMessageSize() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.MaxMessageSize = 1500; var ep = new AmqpTcpEndpoint("localhost"); Assert.Equal(ConnectionFactory.DefaultMaxMessageSize, ep.MaxMessageSize); - using (IConnection conn = cf.CreateConnection(new List { ep })) + using (IConnection conn = await cf.CreateConnectionAsync(new List { ep })) { Assert.Equal(ConnectionFactory.DefaultMaxMessageSize, conn.Endpoint.MaxMessageSize); - }; + await conn.CloseAsync(); + } } [Fact] - public void TestCreateConnectionWithAmqpEndpointResolverUsesAmqpTcpEndpointMaxMessageSize() + public async Task TestCreateConnectionWithAmqpEndpointResolverUsesAmqpTcpEndpointMaxMessageSize() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.MaxMessageSize = 1500; var ep = new AmqpTcpEndpoint("localhost", -1, new SslOption(), 1200); - using (IConnection conn = cf.CreateConnection(new List { ep })) + using (IConnection conn = await cf.CreateConnectionAsync(new List { ep })) { Assert.Equal(ep.MaxMessageSize, conn.Endpoint.MaxMessageSize); - }; + await conn.CloseAsync(); + } } [Fact] - public void TestCreateConnectionWithHostnameListUsesConnectionFactoryMaxMessageSize() + public async Task TestCreateConnectionWithHostnameListUsesConnectionFactoryMaxMessageSize() { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.MaxMessageSize = 1500; - using (IConnection conn = cf.CreateConnection(new List { "localhost" })) + using (IConnection conn = await cf.CreateConnectionAsync(new List { "localhost" })) { Assert.Equal(cf.MaxMessageSize, conn.Endpoint.MaxMessageSize); - }; + await conn.CloseAsync(); + } } } } diff --git a/projects/Test/Integration/TestConnectionFactoryContinuationTimeout.cs b/projects/Test/Integration/TestConnectionFactoryContinuationTimeout.cs index 23022e8d8b..8d9d4890e9 100644 --- a/projects/Test/Integration/TestConnectionFactoryContinuationTimeout.cs +++ b/projects/Test/Integration/TestConnectionFactoryContinuationTimeout.cs @@ -30,6 +30,7 @@ //--------------------------------------------------------------------------- using System; +using System.Threading.Tasks; using RabbitMQ.Client; using Xunit; using Xunit.Abstractions; @@ -42,39 +43,54 @@ public TestConnectionFactoryContinuationTimeout(ITestOutputHelper output) : base { } - protected override void SetUp() + public override Task InitializeAsync() { + // NB: nothing to do here since each test creates its own factory, + // connections and channels + Assert.Null(_connFactory); + Assert.Null(_conn); + Assert.Null(_channel); + return Task.CompletedTask; } [Fact] - public void TestConnectionFactoryContinuationTimeoutOnRecoveringConnection() + public async Task TestConnectionFactoryContinuationTimeoutOnRecoveringConnection() { var continuationTimeout = TimeSpan.FromSeconds(777); - using (IConnection c = CreateConnectionWithContinuationTimeout(true, continuationTimeout)) + using (IConnection c = await CreateConnectionWithContinuationTimeoutAsync(true, continuationTimeout)) { - Assert.Equal(continuationTimeout, c.CreateChannel().ContinuationTimeout); + using (IChannel ch = await c.CreateChannelAsync()) + { + Assert.Equal(continuationTimeout, ch.ContinuationTimeout); + await ch.CloseAsync(); + } + + await c.CloseAsync(); } } [Fact] - public void TestConnectionFactoryContinuationTimeoutOnNonRecoveringConnection() + public async Task TestConnectionFactoryContinuationTimeoutOnNonRecoveringConnection() { var continuationTimeout = TimeSpan.FromSeconds(777); - using (IConnection c = CreateConnectionWithContinuationTimeout(false, continuationTimeout)) + using (IConnection c = await CreateConnectionWithContinuationTimeoutAsync(false, continuationTimeout)) { - using (IChannel ch = c.CreateChannel()) + using (IChannel ch = await c.CreateChannelAsync()) { Assert.Equal(continuationTimeout, ch.ContinuationTimeout); + await ch.CloseAsync(); } + + await c.CloseAsync(); } } - private IConnection CreateConnectionWithContinuationTimeout(bool automaticRecoveryEnabled, TimeSpan continuationTimeout) + private Task CreateConnectionWithContinuationTimeoutAsync(bool automaticRecoveryEnabled, TimeSpan continuationTimeout) { - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = automaticRecoveryEnabled; cf.ContinuationTimeout = continuationTimeout; - return cf.CreateConnection(); + return cf.CreateConnectionAsync(); } } } diff --git a/projects/Test/Integration/TestConnectionShutdown.cs b/projects/Test/Integration/TestConnectionShutdown.cs index 5489ec4611..d38b3d3ee1 100644 --- a/projects/Test/Integration/TestConnectionShutdown.cs +++ b/projects/Test/Integration/TestConnectionShutdown.cs @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------- using System; -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Framing.Impl; using RabbitMQ.Client.Impl; @@ -46,81 +46,88 @@ public TestConnectionShutdown(ITestOutputHelper output) : base(output) } [Fact] - public void TestCleanClosureWithSocketClosedOutOfBand() + public async Task TestCleanClosureWithSocketClosedOutOfBand() { - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); _channel.ChannelShutdown += (channel, args) => { - latch.Set(); + tcs.SetResult(true); }; var c = (AutorecoveringConnection)_conn; - c.CloseFrameHandler(); + await c.CloseFrameHandlerAsync(); - _conn.Close(TimeSpan.FromSeconds(4)); - Wait(latch, TimeSpan.FromSeconds(5), "channel shutdown"); + await _conn.CloseAsync(TimeSpan.FromSeconds(4)); + await WaitAsync(tcs, TimeSpan.FromSeconds(5), "channel shutdown"); } [Fact] - public void TestAbortWithSocketClosedOutOfBand() + public async Task TestAbortWithSocketClosedOutOfBand() { - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); _channel.ChannelShutdown += (channel, args) => { - latch.Set(); + tcs.SetResult(true); }; var c = (AutorecoveringConnection)_conn; - c.CloseFrameHandler(); + await c.CloseFrameHandlerAsync(); + + await _conn.AbortAsync(); - _conn.Abort(); // default Connection.Abort() timeout and then some - Wait(latch, TimeSpan.FromSeconds(6), "channel shutdown"); + await WaitAsync(tcs, TimeSpan.FromSeconds(6), "channel shutdown"); } [Fact] - public void TestDisposedWithSocketClosedOutOfBand() + public async Task TestDisposedWithSocketClosedOutOfBand() { - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); + _channel.ChannelShutdown += (channel, args) => { - latch.Set(); + tcs.SetResult(true); }; var c = (AutorecoveringConnection)_conn; - c.CloseFrameHandler(); + await c.CloseFrameHandlerAsync(); + // TODO this fails due to a race condition caused by abrupt closure of the + // socket frame handler + // await _conn.CloseAsync(); _conn.Dispose(); - Wait(latch, TimeSpan.FromSeconds(3), "channel shutdown"); + _conn = null; + await WaitAsync(tcs, TimeSpan.FromSeconds(3), "channel shutdown"); } [Fact] - public void TestShutdownSignalPropagationToChannels() + public async Task TestShutdownSignalPropagationToChannels() { - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); _channel.ChannelShutdown += (channel, args) => { - latch.Set(); + tcs.SetResult(true); }; - _conn.Close(); - Wait(latch, TimeSpan.FromSeconds(3), "channel shutdown"); + await _conn.CloseAsync(); + + await WaitAsync(tcs, TimeSpan.FromSeconds(3), "channel shutdown"); } [Fact] - public void TestConsumerDispatcherShutdown() + public async Task TestConsumerDispatcherShutdown() { var m = (AutorecoveringChannel)_channel; - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); _channel.ChannelShutdown += (channel, args) => { - latch.Set(); + tcs.SetResult(true); }; Assert.False(m.ConsumerDispatcher.IsShutdown, "dispatcher should NOT be shut down before Close"); - _conn.Close(); - Wait(latch, TimeSpan.FromSeconds(3), "channel shutdown"); + await _conn.CloseAsync(); + await WaitAsync(tcs, TimeSpan.FromSeconds(3), "channel shutdown"); Assert.True(m.ConsumerDispatcher.IsShutdown, "dispatcher should be shut down after Close"); } } diff --git a/projects/Test/Integration/TestConsumer.cs b/projects/Test/Integration/TestConsumer.cs index 9aa3082eb4..681969bc0a 100644 --- a/projects/Test/Integration/TestConsumer.cs +++ b/projects/Test/Integration/TestConsumer.cs @@ -30,6 +30,7 @@ //--------------------------------------------------------------------------- using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using RabbitMQ.Client; @@ -49,65 +50,72 @@ public TestConsumer(ITestOutputHelper output) : base(output) } [Fact] - public void TestBasicRoundtrip() + public async Task TestBasicRoundtrip() { - QueueDeclareOk q = _channel.QueueDeclare(); - _channel.BasicPublish("", q.QueueName, _body); + TimeSpan waitSpan = TimeSpan.FromSeconds(2); + QueueDeclareOk q = await _channel.QueueDeclareAsync(); + await _channel.BasicPublishAsync("", q.QueueName, _body); var consumer = new EventingBasicConsumer(_channel); - var are = new AutoResetEvent(false); - consumer.Received += (o, a) => + using (var consumerReceivedSemaphore = new SemaphoreSlim(0, 1)) + { + consumer.Received += (o, a) => { - are.Set(); + consumerReceivedSemaphore.Release(); }; - string tag = _channel.BasicConsume(q.QueueName, true, consumer); - // ensure we get a delivery - bool waitRes = are.WaitOne(2000); - Assert.True(waitRes); - // unsubscribe and ensure no further deliveries - _channel.BasicCancel(tag); - _channel.BasicPublish("", q.QueueName, _body); - bool waitResFalse = are.WaitOne(2000); - Assert.False(waitResFalse); + string tag = await _channel.BasicConsumeAsync(q.QueueName, true, consumer); + // ensure we get a delivery + bool waitRes = await consumerReceivedSemaphore.WaitAsync(waitSpan); + Assert.True(waitRes); + // unsubscribe and ensure no further deliveries + await _channel.BasicCancelAsync(tag); + await _channel.BasicPublishAsync("", q.QueueName, _body); + bool waitResFalse = await consumerReceivedSemaphore.WaitAsync(waitSpan); + Assert.False(waitResFalse); + } } [Fact] - public void TestBasicRoundtripNoWait() + public async Task TestBasicRoundtripNoWait() { - QueueDeclareOk q = _channel.QueueDeclare(); - _channel.BasicPublish("", q.QueueName, _body); + QueueDeclareOk q = await _channel.QueueDeclareAsync(); + await _channel.BasicPublishAsync("", q.QueueName, _body); var consumer = new EventingBasicConsumer(_channel); - var are = new AutoResetEvent(false); - consumer.Received += (o, a) => + using (var consumerReceivedSemaphore = new SemaphoreSlim(0, 1)) + { + consumer.Received += (o, a) => { - are.Set(); + consumerReceivedSemaphore.Release(); }; - string tag = _channel.BasicConsume(q.QueueName, true, consumer); - // ensure we get a delivery - bool waitRes = are.WaitOne(2000); - Assert.True(waitRes); - // unsubscribe and ensure no further deliveries - _channel.BasicCancelNoWait(tag); - _channel.BasicPublish("", q.QueueName, _body); - bool waitResFalse = are.WaitOne(2000); - Assert.False(waitResFalse); + string tag = await _channel.BasicConsumeAsync(q.QueueName, true, consumer); + // ensure we get a delivery + bool waitRes0 = await consumerReceivedSemaphore.WaitAsync(TimeSpan.FromSeconds(2)); + Assert.True(waitRes0); + // unsubscribe and ensure no further deliveries + await _channel.BasicCancelAsync(tag, noWait: true); + await _channel.BasicPublishAsync("", q.QueueName, _body); + bool waitRes1 = await consumerReceivedSemaphore.WaitAsync(TimeSpan.FromSeconds(2)); + Assert.False(waitRes1); + } } [Fact] - public void ConcurrentEventingTestForReceived() + public async Task ConcurrentEventingTestForReceived() { const int NumberOfThreads = 4; const int NumberOfRegistrations = 5000; - var called = new byte[NumberOfThreads * NumberOfRegistrations]; + byte[] called = new byte[NumberOfThreads * NumberOfRegistrations]; - QueueDeclareOk q = _channel.QueueDeclare(); + QueueDeclareOk q = await _channel.QueueDeclareAsync(); var consumer = new EventingBasicConsumer(_channel); - _channel.BasicConsume(q.QueueName, true, consumer); + await _channel.BasicConsumeAsync(q.QueueName, true, consumer); var countdownEvent = new CountdownEvent(NumberOfThreads); + + var tasks = new List(); for (int i = 0; i < NumberOfThreads; i++) { int threadIndex = i; - Task.Run(() => + tasks.Add(Task.Run(() => { int start = threadIndex * NumberOfRegistrations; for (int j = start; j < start + NumberOfRegistrations; j++) @@ -119,21 +127,23 @@ public void ConcurrentEventingTestForReceived() }; } countdownEvent.Signal(); - }); + })); } countdownEvent.Wait(); // Add last receiver - var are = new AutoResetEvent(false); + var lastConsumerReceivedTcs = new TaskCompletionSource(); consumer.Received += (o, a) => { - are.Set(); + lastConsumerReceivedTcs.SetResult(true); }; // Send message - _channel.BasicPublish("", q.QueueName, ReadOnlyMemory.Empty); - are.WaitOne(TimingFixture.TestTimeout); + await _channel.BasicPublishAsync("", q.QueueName, ReadOnlyMemory.Empty); + + await lastConsumerReceivedTcs.Task.WaitAsync(TimingFixture.TestTimeout); + Assert.True(await lastConsumerReceivedTcs.Task); // Check received messages Assert.Equal(-1, called.AsSpan().IndexOf((byte)0)); diff --git a/projects/Test/Integration/TestConsumerCancelNotify.cs b/projects/Test/Integration/TestConsumerCancelNotify.cs index 630a3e6035..68a64bf1f6 100644 --- a/projects/Test/Integration/TestConsumerCancelNotify.cs +++ b/projects/Test/Integration/TestConsumerCancelNotify.cs @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------- using System.Linq; -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; using Xunit; @@ -40,9 +40,7 @@ namespace Test.Integration { public class TestConsumerCancelNotify : IntegrationFixture { - private readonly ManualResetEventSlim _latch = new ManualResetEventSlim(); - private bool _notifiedCallback; - private bool _notifiedEvent; + private readonly TaskCompletionSource _tcs = new TaskCompletionSource(); private string _consumerTag; public TestConsumerCancelNotify(ITestOutputHelper output) : base(output) @@ -50,53 +48,52 @@ public TestConsumerCancelNotify(ITestOutputHelper output) : base(output) } [Fact] - public void TestConsumerCancelNotification() + public Task TestConsumerCancelNotification() { - TestConsumerCancel("queue_consumer_cancel_notify", false, ref _notifiedCallback); + return TestConsumerCancelAsync("queue_consumer_cancel_notify", false); } [Fact] - public void TestConsumerCancelEvent() + public Task TestConsumerCancelEvent() { - TestConsumerCancel("queue_consumer_cancel_event", true, ref _notifiedEvent); + return TestConsumerCancelAsync("queue_consumer_cancel_event", true); } [Fact] - public void TestCorrectConsumerTag() + public async Task TestCorrectConsumerTag() { string q1 = GenerateQueueName(); string q2 = GenerateQueueName(); - _channel.QueueDeclare(q1, false, false, false, null); - _channel.QueueDeclare(q2, false, false, false, null); + await _channel.QueueDeclareAsync(q1, false, false, false); + await _channel.QueueDeclareAsync(q2, false, false, false); EventingBasicConsumer consumer = new EventingBasicConsumer(_channel); - string consumerTag1 = _channel.BasicConsume(q1, true, consumer); - string consumerTag2 = _channel.BasicConsume(q2, true, consumer); + string consumerTag1 = await _channel.BasicConsumeAsync(q1, true, consumer); + string consumerTag2 = await _channel.BasicConsumeAsync(q2, true, consumer); string notifiedConsumerTag = null; consumer.ConsumerCancelled += (sender, args) => { notifiedConsumerTag = args.ConsumerTags.First(); - _latch.Set(); + _tcs.SetResult(true); }; - _channel.QueueDelete(q1); - Wait(_latch, "ConsumerCancelled event"); + await _channel.QueueDeleteAsync(q1); + await WaitAsync(_tcs, "ConsumerCancelled event"); Assert.Equal(consumerTag1, notifiedConsumerTag); - _channel.QueueDelete(q2); + await _channel.QueueDeleteAsync(q2); } - private void TestConsumerCancel(string queue, bool EventMode, ref bool notified) + private async Task TestConsumerCancelAsync(string queue, bool eventMode) { - _channel.QueueDeclare(queue, false, true, false, null); - IBasicConsumer consumer = new CancelNotificationConsumer(_channel, this, EventMode); - string actualConsumerTag = _channel.BasicConsume(queue, false, consumer); + await _channel.QueueDeclareAsync(queue, false, true, false); + IBasicConsumer consumer = new CancelNotificationConsumer(_channel, this, eventMode); + string actualConsumerTag = await _channel.BasicConsumeAsync(queue, false, consumer); - _channel.QueueDelete(queue); - Wait(_latch, "HandleBasicCancel / Cancelled event"); - Assert.True(notified); + await _channel.QueueDeleteAsync(queue); + await WaitAsync(_tcs, "HandleBasicCancel / Cancelled event"); Assert.Equal(actualConsumerTag, _consumerTag); } @@ -105,12 +102,12 @@ private class CancelNotificationConsumer : DefaultBasicConsumer private readonly TestConsumerCancelNotify _testClass; private readonly bool _eventMode; - public CancelNotificationConsumer(IChannel channel, TestConsumerCancelNotify tc, bool EventMode) + public CancelNotificationConsumer(IChannel channel, TestConsumerCancelNotify tc, bool eventMode) : base(channel) { _testClass = tc; - _eventMode = EventMode; - if (EventMode) + _eventMode = eventMode; + if (eventMode) { ConsumerCancelled += Cancelled; } @@ -120,18 +117,17 @@ public override void HandleBasicCancel(string consumerTag) { if (!_eventMode) { - _testClass._notifiedCallback = true; _testClass._consumerTag = consumerTag; - _testClass._latch.Set(); + _testClass._tcs.SetResult(true); } + base.HandleBasicCancel(consumerTag); } private void Cancelled(object sender, ConsumerEventArgs arg) { - _testClass._notifiedEvent = true; _testClass._consumerTag = arg.ConsumerTags[0]; - _testClass._latch.Set(); + _testClass._tcs.SetResult(true); } } } diff --git a/projects/Test/Integration/TestConsumerCount.cs b/projects/Test/Integration/TestConsumerCount.cs index 8b38ea0360..ab4bb3703f 100644 --- a/projects/Test/Integration/TestConsumerCount.cs +++ b/projects/Test/Integration/TestConsumerCount.cs @@ -29,6 +29,7 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; using Xunit; @@ -43,17 +44,17 @@ public TestConsumerCount(ITestOutputHelper output) : base(output) } [Fact] - public void TestConsumerCountMethod() + public async Task TestConsumerCountMethod() { string q = GenerateQueueName(); - _channel.QueueDeclare(queue: q, durable: false, exclusive: true, autoDelete: false, arguments: null); - Assert.Equal(0u, _channel.ConsumerCount(q)); + await _channel.QueueDeclareAsync(queue: q, durable: false, exclusive: true, autoDelete: false, arguments: null); + Assert.Equal(0u, await _channel.ConsumerCountAsync(q)); - string tag = _channel.BasicConsume(q, true, new EventingBasicConsumer(_channel)); - Assert.Equal(1u, _channel.ConsumerCount(q)); + string tag = await _channel.BasicConsumeAsync(q, true, new EventingBasicConsumer(_channel)); + Assert.Equal(1u, await _channel.ConsumerCountAsync(q)); - _channel.BasicCancel(tag); - Assert.Equal(0u, _channel.ConsumerCount(q)); + await _channel.BasicCancelAsync(tag); + Assert.Equal(0u, await _channel.ConsumerCountAsync(q)); } } } diff --git a/projects/Test/Integration/TestConsumerExceptions.cs b/projects/Test/Integration/TestConsumerExceptions.cs index d8ec356e68..d119eba2c4 100644 --- a/projects/Test/Integration/TestConsumerExceptions.cs +++ b/projects/Test/Integration/TestConsumerExceptions.cs @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------- using System; -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using Xunit; using Xunit.Abstractions; @@ -45,12 +45,12 @@ public ConsumerFailingOnDelivery(IChannel channel) : base(channel) { } - public override void HandleBasicDeliver(string consumerTag, + public override Task HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - in ReadOnlyBasicProperties properties, + ReadOnlyBasicProperties properties, ReadOnlyMemory body) { throw new Exception("oops"); @@ -105,22 +105,22 @@ public override void HandleBasicCancelOk(string consumerTag) } } - protected void TestExceptionHandlingWith(IBasicConsumer consumer, - Action action) + protected async Task TestExceptionHandlingWithAsync(IBasicConsumer consumer, + Func action) { - var mre = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); bool notified = false; - string q = _channel.QueueDeclare(); + string q = await _channel.QueueDeclareAsync(); _channel.CallbackException += (m, evt) => { notified = true; - mre.Set(); + tcs.SetResult(true); }; - string tag = _channel.BasicConsume(q, true, consumer); - action(_channel, q, consumer, tag); - Wait(mre, "callback exception"); + string tag = await _channel.BasicConsumeAsync(q, true, consumer); + await action(_channel, q, consumer, tag); + await WaitAsync(tcs, "callback exception"); Assert.True(notified); } @@ -130,38 +130,42 @@ public TestConsumerExceptions(ITestOutputHelper output) : base(output) } [Fact] - public void TestCancelNotificationExceptionHandling() + public Task TestCancelNotificationExceptionHandling() { IBasicConsumer consumer = new ConsumerFailingOnCancel(_channel); - TestExceptionHandlingWith(consumer, (m, q, c, ct) => m.QueueDelete(q)); + return TestExceptionHandlingWithAsync(consumer, (ch, q, c, ct) => + { + return ch.QueueDeleteAsync(q); + }); } [Fact] - public void TestConsumerCancelOkExceptionHandling() + public Task TestConsumerCancelOkExceptionHandling() { IBasicConsumer consumer = new ConsumerFailingOnCancelOk(_channel); - TestExceptionHandlingWith(consumer, (m, q, c, ct) => m.BasicCancel(ct)); + return TestExceptionHandlingWithAsync(consumer, (ch, q, c, ct) => ch.BasicCancelAsync(ct)); } [Fact] - public void TestConsumerConsumeOkExceptionHandling() + public Task TestConsumerConsumeOkExceptionHandling() { IBasicConsumer consumer = new ConsumerFailingOnConsumeOk(_channel); - TestExceptionHandlingWith(consumer, (m, q, c, ct) => { }); + return TestExceptionHandlingWithAsync(consumer, (ch, q, c, ct) => Task.CompletedTask); } [Fact] - public void TestConsumerShutdownExceptionHandling() + public Task TestConsumerShutdownExceptionHandling() { IBasicConsumer consumer = new ConsumerFailingOnShutdown(_channel); - TestExceptionHandlingWith(consumer, (m, q, c, ct) => m.Close()); + return TestExceptionHandlingWithAsync(consumer, (ch, q, c, ct) => ch.CloseAsync()); } [Fact] - public void TestDeliveryExceptionHandling() + public Task TestDeliveryExceptionHandling() { IBasicConsumer consumer = new ConsumerFailingOnDelivery(_channel); - TestExceptionHandlingWith(consumer, (m, q, c, ct) => m.BasicPublish("", q, _encoding.GetBytes("msg"))); + return TestExceptionHandlingWithAsync(consumer, (ch, q, c, ct) => + ch.BasicPublishAsync("", q, _encoding.GetBytes("msg")).AsTask()); } } } diff --git a/projects/Test/Integration/TestConsumerOperationDispatch.cs b/projects/Test/Integration/TestConsumerOperationDispatch.cs index 3e2588aa88..c90f2e9871 100644 --- a/projects/Test/Integration/TestConsumerOperationDispatch.cs +++ b/projects/Test/Integration/TestConsumerOperationDispatch.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; using Xunit; @@ -58,17 +59,19 @@ public TestConsumerOperationDispatch(ITestOutputHelper output) : base(output) { } - protected override void TearDown() + public override async Task DisposeAsync() { foreach (IChannel ch in _channels) { if (ch.IsOpen) { - ch.Close(); + await ch.CloseAsync(); } } s_counter.Reset(); + + await base.DisposeAsync(); } private class CollectingConsumer : DefaultBasicConsumer @@ -81,9 +84,9 @@ public CollectingConsumer(IChannel channel) DeliveryTags = new List(); } - public override void HandleBasicDeliver(string consumerTag, + public override Task HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - in ReadOnlyBasicProperties properties, ReadOnlyMemory body) + ReadOnlyBasicProperties properties, ReadOnlyMemory body) { // we test concurrent dispatch from the moment basic.delivery is returned. // delivery tags have guaranteed ordering and we verify that it is preserved @@ -95,32 +98,32 @@ public CollectingConsumer(IChannel channel) s_counter.Signal(); } - Channel.BasicAck(deliveryTag: deliveryTag, multiple: false); + return Channel.BasicAckAsync(deliveryTag: deliveryTag, multiple: false).AsTask(); } } [SkippableFact] - public void TestDeliveryOrderingWithSingleChannel() + public async Task TestDeliveryOrderingWithSingleChannel() { Skip.If(IntegrationFixture.IsRunningInCI && IntegrationFixtureBase.IsWindows, "TODO - test is slow in CI on Windows"); - _channel.ExchangeDeclare(_x, "fanout", durable: false); + await _channel.ExchangeDeclareAsync(_x, "fanout", durable: false); for (int i = 0; i < Y; i++) { - IChannel ch = _conn.CreateChannel(); - QueueDeclareOk q = ch.QueueDeclare("", durable: false, exclusive: true, autoDelete: true, arguments: null); - ch.QueueBind(queue: q, exchange: _x, routingKey: ""); + IChannel ch = await _conn.CreateChannelAsync(); + QueueDeclareOk q = await ch.QueueDeclareAsync("", durable: false, exclusive: true, autoDelete: true, arguments: null); + await ch.QueueBindAsync(queue: q, exchange: _x, routingKey: ""); _channels.Add(ch); _queues.Add(q); var cons = new CollectingConsumer(ch); _consumers.Add(cons); - ch.BasicConsume(queue: q, autoAck: false, consumer: cons); + await ch.BasicConsumeAsync(queue: q, autoAck: false, consumer: cons); } for (int i = 0; i < N; i++) { - _channel.BasicPublish(_x, "", _encoding.GetBytes("msg")); + await _channel.BasicPublishAsync(_x, "", _encoding.GetBytes("msg")); } if (IntegrationFixture.IsRunningInCI) @@ -150,68 +153,75 @@ public void TestDeliveryOrderingWithSingleChannel() // see rabbitmq/rabbitmq-dotnet-client#61 [Fact] - public void TestChannelShutdownDoesNotShutDownDispatcher() + public async Task TestChannelShutdownDoesNotShutDownDispatcher() { - _channel.ExchangeDeclare(_x, "fanout", durable: false); + await _channel.ExchangeDeclareAsync(_x, "fanout", durable: false); - IChannel ch1 = _conn.CreateChannel(); - IChannel ch2 = _conn.CreateChannel(); + IChannel ch1 = await _conn.CreateChannelAsync(); + IChannel ch2 = await _conn.CreateChannelAsync(); - string q1 = ch1.QueueDeclare().QueueName; - string q2 = ch2.QueueDeclare().QueueName; - ch2.QueueBind(queue: q2, exchange: _x, routingKey: ""); + string q1 = (await ch1.QueueDeclareAsync()).QueueName; + string q2 = (await ch2.QueueDeclareAsync()).QueueName; + await ch2.QueueBindAsync(queue: q2, exchange: _x, routingKey: ""); - var latch = new ManualResetEventSlim(false); - ch1.BasicConsume(q1, true, new EventingBasicConsumer(ch1)); + var tcs = new TaskCompletionSource(); + await ch1.BasicConsumeAsync(q1, true, new EventingBasicConsumer(ch1)); var c2 = new EventingBasicConsumer(ch2); c2.Received += (object sender, BasicDeliverEventArgs e) => { - latch.Set(); + tcs.SetResult(true); }; - ch2.BasicConsume(q2, true, c2); + await ch2.BasicConsumeAsync(q2, true, c2); // closing this channel must not affect ch2 - ch1.Close(); + await ch1.CloseAsync(); - ch2.BasicPublish(_x, "", _encoding.GetBytes("msg")); - Wait(latch, "received event"); + await ch2.BasicPublishAsync(_x, "", _encoding.GetBytes("msg")); + await WaitAsync(tcs, "received event"); } private class ShutdownLatchConsumer : DefaultBasicConsumer { public ShutdownLatchConsumer() { - Latch = new(); - DuplicateLatch = new(); + Latch = new TaskCompletionSource(); + DuplicateLatch = new TaskCompletionSource(); } - public readonly ManualResetEventSlim Latch; - public readonly ManualResetEventSlim DuplicateLatch; + public readonly TaskCompletionSource Latch; + public readonly TaskCompletionSource DuplicateLatch; public override void HandleChannelShutdown(object channel, ShutdownEventArgs reason) { // keep track of duplicates - if (Latch.Wait(0)) + if (Latch.Task.IsCompletedSuccessfully()) { - DuplicateLatch.Set(); + DuplicateLatch.SetResult(true); } else { - Latch.Set(); + Latch.SetResult(true); } } } [Fact] - public void TestChannelShutdownHandler() + public async Task TestChannelShutdownHandler() { - string q = _channel.QueueDeclare(); + string q = await _channel.QueueDeclareAsync(); var c = new ShutdownLatchConsumer(); - _channel.BasicConsume(queue: q, autoAck: true, consumer: c); - _channel.Close(); + await _channel.BasicConsumeAsync(queue: q, autoAck: true, consumer: c); + await _channel.CloseAsync(); + + await c.Latch.Task.WaitAsync(TimeSpan.FromSeconds(5)); + Assert.True(c.Latch.Task.IsCompletedSuccessfully()); + + await Assert.ThrowsAsync(() => + { + return c.DuplicateLatch.Task.WaitAsync(TimeSpan.FromSeconds(5)); + }); - Wait(c.Latch, TimeSpan.FromSeconds(5), "channel shutdown"); - Assert.False(c.DuplicateLatch.Wait(TimeSpan.FromSeconds(5)), "event handler fired more than once"); + Assert.False(c.DuplicateLatch.Task.IsCompletedSuccessfully()); } } } diff --git a/projects/Test/Integration/TestEventingConsumer.cs b/projects/Test/Integration/TestEventingConsumer.cs index f3e33f1908..4ee805dd2f 100644 --- a/projects/Test/Integration/TestEventingConsumer.cs +++ b/projects/Test/Integration/TestEventingConsumer.cs @@ -29,7 +29,7 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; using Xunit; @@ -44,47 +44,49 @@ public TestEventingConsumer(ITestOutputHelper output) : base(output) } [Fact] - public void TestEventingConsumerRegistrationEvents() + public async Task TestEventingConsumerRegistrationEvents() { - string q = _channel.QueueDeclare(); + string q = await _channel.QueueDeclareAsync(); - var registeredLatch = new ManualResetEventSlim(false); + var registeredTcs = new TaskCompletionSource(); object registeredSender = null; - var unregisteredLatch = new ManualResetEventSlim(false); + + var unregisteredTcs = new TaskCompletionSource(); object unregisteredSender = null; EventingBasicConsumer ec = new EventingBasicConsumer(_channel); ec.Registered += (s, args) => { registeredSender = s; - registeredLatch.Set(); + registeredTcs.SetResult(true); }; ec.Unregistered += (s, args) => { unregisteredSender = s; - unregisteredLatch.Set(); + unregisteredTcs.SetResult(true); }; - string tag = _channel.BasicConsume(q, false, ec); - Wait(registeredLatch, "consumer registered"); + string tag = await _channel.BasicConsumeAsync(q, false, ec); + await WaitAsync(registeredTcs, "consumer registered"); Assert.NotNull(registeredSender); Assert.Equal(ec, registeredSender); Assert.Equal(_channel, ((EventingBasicConsumer)registeredSender).Channel); - _channel.BasicCancel(tag); - Wait(unregisteredLatch, "consumer unregistered"); + await _channel.BasicCancelAsync(tag); + + await WaitAsync(unregisteredTcs, "consumer unregistered"); Assert.NotNull(unregisteredSender); Assert.Equal(ec, unregisteredSender); Assert.Equal(_channel, ((EventingBasicConsumer)unregisteredSender).Channel); } [Fact] - public void TestEventingConsumerDeliveryEvents() + public async Task TestEventingConsumerDeliveryEvents() { - var mre = new ManualResetEventSlim(false); - string q = _channel.QueueDeclare(); + var tcs0 = new TaskCompletionSource(); + string q = await _channel.QueueDeclareAsync(); bool receivedInvoked = false; object receivedSender = null; @@ -94,14 +96,14 @@ public void TestEventingConsumerDeliveryEvents() { receivedInvoked = true; receivedSender = s; - mre.Set(); + tcs0.SetResult(true); }; - _channel.BasicConsume(q, true, ec); - _channel.BasicPublish("", q, _encoding.GetBytes("msg")); + await _channel.BasicConsumeAsync(q, true, ec); + await _channel.BasicPublishAsync("", q, _encoding.GetBytes("msg")); - Wait(mre, "received event"); - mre.Reset(); + await WaitAsync(tcs0, "received event"); + var tcs1 = new TaskCompletionSource(); Assert.True(receivedInvoked); Assert.NotNull(receivedSender); @@ -115,12 +117,12 @@ public void TestEventingConsumerDeliveryEvents() { shutdownInvoked = true; shutdownSender = s; - mre.Set(); + tcs1.SetResult(true); }; - _channel.Close(); + await _channel.CloseAsync(); - Wait(mre, "shutdown event"); + await WaitAsync(tcs1, "shutdown event"); Assert.True(shutdownInvoked); Assert.NotNull(shutdownSender); diff --git a/projects/Test/Integration/TestExceptionMessages.cs b/projects/Test/Integration/TestExceptionMessages.cs index 7901096f54..fdf3a55785 100644 --- a/projects/Test/Integration/TestExceptionMessages.cs +++ b/projects/Test/Integration/TestExceptionMessages.cs @@ -30,6 +30,7 @@ //--------------------------------------------------------------------------- using System; +using System.Threading.Tasks; using RabbitMQ.Client.Exceptions; using Xunit; using Xunit.Abstractions; @@ -43,12 +44,12 @@ public TestExceptionMessages(ITestOutputHelper output) : base(output) } [Fact] - public void TestAlreadyClosedExceptionMessage() + public async Task TestAlreadyClosedExceptionMessage() { - string uuid = System.Guid.NewGuid().ToString(); + string uuid = Guid.NewGuid().ToString(); try { - _channel.QueueDeclarePassive(uuid); + await _channel.QueueDeclarePassiveAsync(uuid); } catch (Exception e) { @@ -59,7 +60,7 @@ public void TestAlreadyClosedExceptionMessage() try { - _channel.QueueDeclarePassive(uuid); + await _channel.QueueDeclarePassiveAsync(uuid); } catch (AlreadyClosedException e) { diff --git a/projects/Test/Integration/TestExchangeDeclare.cs b/projects/Test/Integration/TestExchangeDeclare.cs index 47056d76ed..3fdbb45462 100644 --- a/projects/Test/Integration/TestExchangeDeclare.cs +++ b/projects/Test/Integration/TestExchangeDeclare.cs @@ -31,8 +31,7 @@ using System; using System.Collections.Generic; -using System.Threading; -using RabbitMQ.Client; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -47,22 +46,22 @@ public TestExchangeDeclare(ITestOutputHelper output) : base(output) } [Fact] - public void TestConcurrentExchangeDeclareAndDelete() + public async Task TestConcurrentExchangeDeclareAndDelete() { var exchangeNames = new List(); - var ts = new List(); + var tasks = new List(); NotSupportedException nse = null; for (int i = 0; i < 256; i++) { - var t = new Thread(() => + var t = Task.Run(async () => { try { // sleep for a random amount of time to increase the chances // of thread interleaving. MK. - Thread.Sleep(_rnd.Next(5, 500)); + await Task.Delay(_rnd.Next(5, 500)); string exchangeName = GenerateExchangeName(); - _channel.ExchangeDeclare(exchange: exchangeName, "fanout", false, false, null); + await _channel.ExchangeDeclareAsync(exchange: exchangeName, "fanout", false, false); exchangeNames.Add(exchangeName); } catch (NotSupportedException e) @@ -70,42 +69,35 @@ public void TestConcurrentExchangeDeclareAndDelete() nse = e; } }); - ts.Add(t); - t.Start(); + tasks.Add(t); } - foreach (Thread t in ts) - { - t.Join(); - } + await Task.WhenAll(tasks); Assert.Null(nse); - ts.Clear(); + tasks.Clear(); foreach (string exchangeName in exchangeNames) { - var t = new Thread((object ex) => + string ex = exchangeName; + var t = Task.Run(async () => { try { // sleep for a random amount of time to increase the chances // of thread interleaving. MK. - Thread.Sleep(_rnd.Next(5, 500)); - _channel.ExchangeDelete((string)ex); + await Task.Delay(_rnd.Next(5, 500)); + await _channel.ExchangeDeleteAsync(ex); } catch (NotSupportedException e) { nse = e; } }); - ts.Add(t); - t.Start(exchangeName); + tasks.Add(t); } - foreach (Thread t in ts) - { - t.Join(); - } + await Task.WhenAll(tasks); Assert.Null(nse); } diff --git a/projects/Test/Integration/TestHeartbeats.cs b/projects/Test/Integration/TestHeartbeats.cs index 9d2bba292c..93f29bed39 100644 --- a/projects/Test/Integration/TestHeartbeats.cs +++ b/projects/Test/Integration/TestHeartbeats.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using Xunit; using Xunit.Abstractions; @@ -46,36 +47,39 @@ public TestHeartbeats(ITestOutputHelper output) : base(output) { } - protected override void SetUp() + public override Task InitializeAsync() { + // NB: nothing to do here since each test creates its own factory, + // connections and channels Assert.Null(_connFactory); Assert.Null(_conn); Assert.Null(_channel); + return Task.CompletedTask; } [SkippableFact(Timeout = 35000)] [Trait("Category", "LongRunning")] - public void TestThatHeartbeatWriterUsesConfigurableInterval() + public async Task TestThatHeartbeatWriterUsesConfigurableInterval() { Skip.IfNot(LongRunningTestsEnabled(), "RABBITMQ_LONG_RUNNING_TESTS is not set, skipping test"); - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.RequestedHeartbeat = _heartbeatTimeout; cf.AutomaticRecoveryEnabled = false; - RunSingleConnectionTest(cf); + await RunSingleConnectionTestAsync(cf); } [SkippableFact] [Trait("Category", "LongRunning")] - public void TestThatHeartbeatWriterWithTLSEnabled() + public async Task TestThatHeartbeatWriterWithTLSEnabled() { Skip.IfNot(LongRunningTestsEnabled(), "RABBITMQ_LONG_RUNNING_TESTS is not set, skipping test"); var sslEnv = new SslEnv(); Skip.IfNot(sslEnv.IsSslConfigured, "SSL_CERTS_DIR and/or PASSWORD are not configured, skipping test"); - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.Port = 5671; cf.RequestedHeartbeat = _heartbeatTimeout; cf.AutomaticRecoveryEnabled = false; @@ -85,12 +89,12 @@ public void TestThatHeartbeatWriterWithTLSEnabled() cf.Ssl.CertPassphrase = sslEnv.CertPassphrase; cf.Ssl.Enabled = true; - RunSingleConnectionTest(cf); + await RunSingleConnectionTestAsync(cf); } [SkippableFact(Timeout = 90000)] [Trait("Category", "LongRunning")] - public void TestHundredsOfConnectionsWithRandomHeartbeatInterval() + public async Task TestHundredsOfConnectionsWithRandomHeartbeatInterval() { Skip.IfNot(LongRunningTestsEnabled(), "RABBITMQ_LONG_RUNNING_TESTS is not set, skipping test"); @@ -110,25 +114,26 @@ public void TestHundredsOfConnectionsWithRandomHeartbeatInterval() for (int i = 0; i < connectionCount; i++) { ushort n = Convert.ToUInt16(rnd.Next(2, 6)); - var cf = CreateConnectionFactory(); + ConnectionFactory cf = CreateConnectionFactory(); cf.RequestedHeartbeat = TimeSpan.FromSeconds(n); cf.AutomaticRecoveryEnabled = false; - IConnection conn = cf.CreateConnection($"{_testDisplayName}:{i}"); + IConnection conn = await cf.CreateConnectionAsync($"{_testDisplayName}:{i}"); conns.Add(conn); - IChannel ch = conn.CreateChannel(); + IChannel ch = await conn.CreateChannelAsync(); conn.ConnectionShutdown += (sender, evt) => { CheckInitiator(evt); }; } - SleepFor(60); + + await SleepFor(60); } finally { foreach (IConnection conn in conns) { - conn.Close(); + await conn.CloseAsync(); } } } @@ -138,11 +143,11 @@ public void TestHundredsOfConnectionsWithRandomHeartbeatInterval() } } - private void RunSingleConnectionTest(ConnectionFactory cf) + private async Task RunSingleConnectionTestAsync(ConnectionFactory cf) { - using (IConnection conn = cf.CreateConnection(_testDisplayName)) + using (IConnection conn = await cf.CreateConnectionAsync(_testDisplayName)) { - using (IChannel ch = conn.CreateChannel()) + using (IChannel ch = await conn.CreateChannelAsync()) { bool wasShutdown = false; @@ -157,11 +162,16 @@ private void RunSingleConnectionTest(ConnectionFactory cf) } } }; - SleepFor(30); + + await SleepFor(30); Assert.False(wasShutdown, "shutdown event should not have been fired"); Assert.True(conn.IsOpen, "connection should be open"); + + await ch.CloseAsync(); } + + await conn.CloseAsync(); } } @@ -182,10 +192,10 @@ private bool LongRunningTestsEnabled() return false; } - private void SleepFor(int t) + private Task SleepFor(int t) { _output.WriteLine("Testing heartbeats, sleeping for {0} seconds", t); - Thread.Sleep(t * 1000); + return Task.Delay(t * 1000); } private bool InitiatedByPeerOrLibrary(ShutdownEventArgs evt) diff --git a/projects/Test/Integration/TestInitialConnection.cs b/projects/Test/Integration/TestInitialConnection.cs index c141baa8f5..267d0b61c8 100644 --- a/projects/Test/Integration/TestInitialConnection.cs +++ b/projects/Test/Integration/TestInitialConnection.cs @@ -30,8 +30,10 @@ //--------------------------------------------------------------------------- using System.Collections.Generic; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Exceptions; +using RabbitMQ.Client.Framing.Impl; using Xunit; using Xunit.Abstractions; @@ -44,27 +46,27 @@ public TestInitialConnection(ITestOutputHelper output) : base(output) } [Fact] - public void TestBasicConnectionRecoveryWithHostnameList() + public async Task TestBasicConnectionRecoveryWithHostnameList() { - var c = CreateAutorecoveringConnection(new List() { "127.0.0.1", "localhost" }); + AutorecoveringConnection c = await CreateAutorecoveringConnectionAsync(new List() { "127.0.0.1", "localhost" }); Assert.True(c.IsOpen); - c.Close(); + await c.CloseAsync(); } [Fact] - public void TestBasicConnectionRecoveryWithHostnameListAndUnreachableHosts() + public async Task TestBasicConnectionRecoveryWithHostnameListAndUnreachableHosts() { - var c = CreateAutorecoveringConnection(new List() { "191.72.44.22", "127.0.0.1", "localhost" }); + AutorecoveringConnection c = await CreateAutorecoveringConnectionAsync(new List() { "191.72.44.22", "127.0.0.1", "localhost" }); Assert.True(c.IsOpen); - c.Close(); + await c.CloseAsync(); } [Fact] - public void TestBasicConnectionRecoveryWithHostnameListWithOnlyUnreachableHosts() + public async Task TestBasicConnectionRecoveryWithHostnameListWithOnlyUnreachableHosts() { - Assert.Throws(() => + await Assert.ThrowsAsync(() => { - CreateAutorecoveringConnection(new List() { + return CreateAutorecoveringConnectionAsync(new List() { "191.72.44.22", "145.23.22.18", "192.255.255.255" diff --git a/projects/Test/Integration/TestInvalidAck.cs b/projects/Test/Integration/TestInvalidAck.cs index d2f2888e88..1eb5d735a8 100644 --- a/projects/Test/Integration/TestInvalidAck.cs +++ b/projects/Test/Integration/TestInvalidAck.cs @@ -29,7 +29,7 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using Xunit; using Xunit.Abstractions; @@ -43,20 +43,20 @@ public TestInvalidAck(ITestOutputHelper output) : base(output) } [Fact] - public void TestAckWithUnknownConsumerTagAndMultipleFalse() + public async Task TestAckWithUnknownConsumerTagAndMultipleFalse() { - var mre = new ManualResetEventSlim(); + var tcs = new TaskCompletionSource(); bool shutdownFired = false; ShutdownEventArgs shutdownArgs = null; _channel.ChannelShutdown += (s, args) => { shutdownFired = true; shutdownArgs = args; - mre.Set(); + tcs.SetResult(true); }; - _channel.BasicAck(123456, false); - Wait(mre, "ChannelShutdown"); + await _channel.BasicAckAsync(123456, false); + await WaitAsync(tcs, "ChannelShutdown"); Assert.True(shutdownFired); AssertPreconditionFailed(shutdownArgs); } diff --git a/projects/Test/Integration/TestMainLoop.cs b/projects/Test/Integration/TestMainLoop.cs index c01f385d92..eda581fc37 100644 --- a/projects/Test/Integration/TestMainLoop.cs +++ b/projects/Test/Integration/TestMainLoop.cs @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------- using System; -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; using Xunit; @@ -48,12 +48,12 @@ private sealed class FaultyConsumer : DefaultBasicConsumer { public FaultyConsumer(IChannel channel) : base(channel) { } - public override void HandleBasicDeliver(string consumerTag, + public override Task HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - in ReadOnlyBasicProperties properties, + ReadOnlyBasicProperties properties, ReadOnlyMemory body) { throw new Exception("I am a bad consumer"); @@ -61,23 +61,23 @@ private sealed class FaultyConsumer : DefaultBasicConsumer } [Fact] - public void TestCloseWithFaultyConsumer() + public async Task TestCloseWithFaultyConsumer() { - var mre = new ManualResetEventSlim(); - QueueDeclareOk q = _channel.QueueDeclare(string.Empty, false, false, false, null); + var tcs = new TaskCompletionSource(); + QueueDeclareOk q = await _channel.QueueDeclareAsync(string.Empty, false, false, false); CallbackExceptionEventArgs ea = null; - _channel.CallbackException += (_, evt) => + _channel.CallbackException += async (_, evt) => { ea = evt; - _channel.Close(); - mre.Set(); + await _channel.CloseAsync(); + tcs.SetResult(true); }; - _channel.BasicConsume(q, true, new FaultyConsumer(_channel)); - _channel.BasicPublish(string.Empty, q, _encoding.GetBytes("message")); + await _channel.BasicConsumeAsync(q, true, new FaultyConsumer(_channel)); + await _channel.BasicPublishAsync(string.Empty, q, _encoding.GetBytes("message")); - Wait(mre, "CallbackException"); + await WaitAsync(tcs, "CallbackException"); Assert.NotNull(ea); Assert.False(_channel.IsOpen); diff --git a/projects/Test/Integration/TestNowait.cs b/projects/Test/Integration/TestNowait.cs index 4fa6f06b30..e865ede5ea 100644 --- a/projects/Test/Integration/TestNowait.cs +++ b/projects/Test/Integration/TestNowait.cs @@ -29,7 +29,9 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- -using RabbitMQ.Client; +using System; +using System.Threading.Tasks; +using RabbitMQ.Client.Exceptions; using Xunit; using Xunit.Abstractions; @@ -42,81 +44,95 @@ public TestNoWait(ITestOutputHelper output) : base(output) } [Fact] - public void TestQueueDeclareNoWait() + public async Task TestQueueDeclareNoWait() { string q = GenerateQueueName(); - _channel.QueueDeclareNoWait(q, false, true, false, null); - _channel.QueueDeclarePassive(q); + await _channel.QueueDeclareAsync(q, false, true, false, noWait: true, arguments: null); + await _channel.QueueDeclarePassiveAsync(q); } [Fact] - public void TestQueueBindNoWait() + public Task TestQueueDeclareServerNamedNoWait() + { + return Assert.ThrowsAsync(() => + { + return _channel.QueueDeclareAsync("", false, true, false, noWait: true); + }); + } + + [Fact] + public async Task TestQueueBindNoWait() { string q = GenerateQueueName(); - _channel.QueueDeclareNoWait(q, false, true, false, null); - _channel.QueueBindNoWait(q, "amq.fanout", "", null); + await _channel.QueueDeclareAsync(q, false, true, false, noWait: true); + await _channel.QueueBindAsync(q, "amq.fanout", "", noWait: true); } [Fact] - public void TestQueueDeleteNoWait() + public async Task TestQueueDeleteNoWait() { string q = GenerateQueueName(); - _channel.QueueDeclareNoWait(q, false, true, false, null); - _channel.QueueDeleteNoWait(q, false, false); + await _channel.QueueDeclareAsync(q, false, true, false, noWait: true); + await _channel.QueueDeleteAsync(q, false, false, noWait: true); + await Assert.ThrowsAsync(() => + { + return _channel.QueueDeclarePassiveAsync(q); + }); } [Fact] - public void TestExchangeDeclareNoWait() + public async Task TestExchangeDeclareNoWaitAsync() { string x = GenerateExchangeName(); try { - _channel.ExchangeDeclareNoWait(x, "fanout", false, true, null); - _channel.ExchangeDeclarePassive(x); + await _channel.ExchangeDeclareAsync(x, "fanout", false, true); + await _channel.ExchangeDeclarePassiveAsync(x); } finally { - _channel.ExchangeDelete(x); + await _channel.ExchangeDeleteAsync(x); } } [Fact] - public void TestExchangeBindNoWait() + public async Task TestExchangeBindNoWait() { string x = GenerateExchangeName(); try { - _channel.ExchangeDeclareNoWait(x, "fanout", false, true, null); - _channel.ExchangeBindNoWait(x, "amq.fanout", "", null); + await _channel.ExchangeDeclareAsync(x, "fanout", false, true, noWait: true); + await _channel.ExchangeBindAsync(x, "amq.fanout", "", noWait: true); } finally { - _channel.ExchangeDelete(x); + await _channel.ExchangeDeleteAsync(x); } } [Fact] - public void TestExchangeUnbindNoWait() + public async Task TestExchangeUnbindNoWait() { string x = GenerateExchangeName(); try { - _channel.ExchangeDeclare(x, "fanout", false, true, null); - _channel.ExchangeBind(x, "amq.fanout", "", null); - _channel.ExchangeUnbindNoWait(x, "amq.fanout", "", null); + await _channel.ExchangeDeclareAsync(x, "fanout", false, true); + await _channel.ExchangeBindAsync(x, "amq.fanout", "", noWait: true); + await _channel.ExchangeUnbindAsync(x, "amq.fanout", "", noWait: true); } finally { - _channel.ExchangeDelete(x); + await _channel.ExchangeDeleteAsync(x); } } [Fact] - public void TestExchangeDeleteNoWait() + public async Task TestExchangeDeleteNoWait() { string x = GenerateExchangeName(); - _channel.ExchangeDeclareNoWait(x, "fanout", false, true, null); - _channel.ExchangeDeleteNoWait(x, false); + await _channel.ExchangeDeclareAsync(x, "fanout", false, true, + noWait: true, arguments: null); + await _channel.ExchangeDeleteAsync(x, false, noWait: true); } } } diff --git a/projects/Test/Integration/TestPassiveDeclare.cs b/projects/Test/Integration/TestPassiveDeclare.cs index 6063697159..3f62505d22 100644 --- a/projects/Test/Integration/TestPassiveDeclare.cs +++ b/projects/Test/Integration/TestPassiveDeclare.cs @@ -30,6 +30,7 @@ //--------------------------------------------------------------------------- using System; +using System.Threading.Tasks; using RabbitMQ.Client.Exceptions; using Xunit; using Xunit.Abstractions; @@ -43,15 +44,21 @@ public TestPassiveDeclare(ITestOutputHelper output) : base(output) } [Fact] - public void TestPassiveExchangeDeclareWhenExchangeDoesNotExist() + public Task TestPassiveExchangeDeclareWhenExchangeDoesNotExist() { - Assert.Throws(() => _channel.ExchangeDeclarePassive(Guid.NewGuid().ToString())); + return Assert.ThrowsAsync(() => + { + return _channel.ExchangeDeclarePassiveAsync(Guid.NewGuid().ToString()); + }); } [Fact] - public void TestPassiveQueueDeclareWhenQueueDoesNotExist() + public Task TestPassiveQueueDeclareWhenQueueDoesNotExist() { - Assert.Throws(() => _channel.QueueDeclarePassive(Guid.NewGuid().ToString())); + return Assert.ThrowsAsync(() => + { + return _channel.QueueDeclarePassiveAsync(Guid.NewGuid().ToString()); + }); } } } diff --git a/projects/Test/Integration/TestQueueDeclare.cs b/projects/Test/Integration/TestQueueDeclare.cs index d068993c63..9d461873ea 100644 --- a/projects/Test/Integration/TestQueueDeclare.cs +++ b/projects/Test/Integration/TestQueueDeclare.cs @@ -31,7 +31,7 @@ using System; using System.Collections.Generic; -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using Xunit; using Xunit.Abstractions; @@ -45,22 +45,22 @@ public TestQueueDeclare(ITestOutputHelper output) : base(output) } [Fact] - public void TestConcurrentQueueDeclare() + public async Task TestConcurrentQueueDeclare() { var qs = new List(); - var ts = new List(); + var tasks = new List(); NotSupportedException nse = null; for (int i = 0; i < 256; i++) { - var t = new Thread(() => + var t = Task.Run(async () => { try { // sleep for a random amount of time to increase the chances // of thread interleaving. MK. - Thread.Sleep(S_Random.Next(5, 50)); + await Task.Delay(S_Random.Next(5, 50)); string q = GenerateQueueName(); - _channel.QueueDeclare(q, false, false, false, null); + await _channel.QueueDeclareAsync(q, false, false, false); qs.Add(q); } catch (NotSupportedException e) @@ -68,40 +68,33 @@ public void TestConcurrentQueueDeclare() nse = e; } }); - ts.Add(t); - t.Start(); + tasks.Add(t); } - foreach (Thread t in ts) - { - t.Join(); - } + await Task.WhenAll(tasks); Assert.Null(nse); - ts.Clear(); + tasks.Clear(); foreach (string queueName in qs) { - var t = new Thread(() => + string q = queueName; + var t = Task.Run(async () => { try { - Thread.Sleep(S_Random.Next(5, 50)); - _channel.QueueDelete(queueName); + await Task.Delay(S_Random.Next(5, 50)); + await _channel.QueueDeleteAsync(queueName); } catch (NotSupportedException e) { nse = e; } }); - ts.Add(t); - t.Start(); + tasks.Add(t); } - foreach (Thread t in ts) - { - t.Join(); - } + await Task.WhenAll(tasks); Assert.Null(nse); } diff --git a/projects/Test/Integration/TestSsl.cs b/projects/Test/Integration/TestSsl.cs index 58ce7fb561..aa7f8ed965 100644 --- a/projects/Test/Integration/TestSsl.cs +++ b/projects/Test/Integration/TestSsl.cs @@ -32,6 +32,7 @@ using System.IO; using System.Net.Security; using System.Security.Authentication; +using System.Threading.Tasks; using RabbitMQ.Client; using Xunit; using Xunit.Abstractions; @@ -47,15 +48,16 @@ public TestSsl(ITestOutputHelper output) : base(output) _sslEnv = new SslEnv(); } - protected override void SetUp() + public override Task InitializeAsync() { Assert.Null(_connFactory); Assert.Null(_conn); Assert.Null(_channel); + return Task.CompletedTask; } [SkippableFact] - public void TestServerVerifiedIgnoringNameMismatch() + public async Task TestServerVerifiedIgnoringNameMismatch() { Skip.IfNot(_sslEnv.IsSslConfigured, "SSL_CERTS_DIR and/or PASSWORD are not configured, skipping test"); @@ -65,11 +67,11 @@ public void TestServerVerifiedIgnoringNameMismatch() cf.Ssl.AcceptablePolicyErrors = SslPolicyErrors.RemoteCertificateNameMismatch; cf.Ssl.Enabled = true; - SendReceive(cf); + await SendReceiveAsync(cf); } [SkippableFact] - public void TestServerVerified() + public async Task TestServerVerified() { Skip.IfNot(_sslEnv.IsSslConfigured, "SSL_CERTS_DIR and/or PASSWORD are not configured, skipping test"); @@ -78,11 +80,11 @@ public void TestServerVerified() cf.Ssl.ServerName = _sslEnv.Hostname; cf.Ssl.Enabled = true; - SendReceive(cf); + await SendReceiveAsync(cf); } [SkippableFact] - public void TestClientAndServerVerified() + public async Task TestClientAndServerVerified() { Skip.IfNot(_sslEnv.IsSslConfigured, "SSL_CERTS_DIR and/or PASSWORD are not configured, skipping test"); @@ -96,12 +98,12 @@ public void TestClientAndServerVerified() cf.Ssl.CertPassphrase = _sslEnv.CertPassphrase; cf.Ssl.Enabled = true; - SendReceive(cf); + await SendReceiveAsync(cf); } // rabbitmq/rabbitmq-dotnet-client#46, also #44 and #45 [SkippableFact] - public void TestNoClientCertificate() + public async Task TestNoClientCertificate() { Skip.IfNot(_sslEnv.IsSslConfigured, "SSL_CERTS_DIR and/or PASSWORD are not configured, skipping test"); @@ -118,29 +120,32 @@ public void TestNoClientCertificate() SslPolicyErrors.RemoteCertificateNameMismatch }; - SendReceive(cf); + await SendReceiveAsync(cf); } - private void SendReceive(ConnectionFactory connectionFactory) + private async Task SendReceiveAsync(ConnectionFactory connectionFactory) { - using (IConnection conn = CreateConnectionWithRetries(connectionFactory)) + using (IConnection conn = await CreateConnectionAsyncWithRetries(connectionFactory)) { - using (IChannel ch = conn.CreateChannel()) + using (IChannel ch = await conn.CreateChannelAsync()) { - ch.ExchangeDeclare("Exchange_TestSslEndPoint", ExchangeType.Direct); - string qName = ch.QueueDeclare(); - ch.QueueBind(qName, "Exchange_TestSslEndPoint", "Key_TestSslEndpoint", null); + await ch.ExchangeDeclareAsync("Exchange_TestSslEndPoint", ExchangeType.Direct); + + string qName = await ch.QueueDeclareAsync(); + await ch.QueueBindAsync(qName, "Exchange_TestSslEndPoint", "Key_TestSslEndpoint"); string message = "Hello C# SSL Client World"; byte[] msgBytes = _encoding.GetBytes(message); - ch.BasicPublish("Exchange_TestSslEndPoint", "Key_TestSslEndpoint", msgBytes); + await ch.BasicPublishAsync("Exchange_TestSslEndPoint", "Key_TestSslEndpoint", msgBytes); bool autoAck = false; - BasicGetResult result = ch.BasicGet(qName, autoAck); + BasicGetResult result = await ch.BasicGetAsync(qName, autoAck); byte[] body = result.Body.ToArray(); string resultMessage = _encoding.GetString(body); Assert.Equal(message, resultMessage); + + await ch.CloseAsync(); } } } diff --git a/projects/Test/Integration/TestUpdateSecret.cs b/projects/Test/Integration/TestUpdateSecret.cs index d2396ca831..3f65d79d64 100644 --- a/projects/Test/Integration/TestUpdateSecret.cs +++ b/projects/Test/Integration/TestUpdateSecret.cs @@ -29,6 +29,8 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- +using System.Threading.Tasks; +using Xunit; using Xunit.Abstractions; namespace Test.Integration @@ -39,9 +41,10 @@ public TestUpdateSecret(ITestOutputHelper output) : base(output) { } - public void TestUpdatingConnectionSecret() + [Fact] + public Task TestUpdatingConnectionSecret() { - _conn.UpdateSecret("new-secret", "Test Case"); + return _conn.UpdateSecretAsync("new-secret", "Test Case"); } } } diff --git a/projects/Test/OAuth2/OAuth2.csproj b/projects/Test/OAuth2/OAuth2.csproj index f7f9b8d2fa..aea8b6d1ba 100644 --- a/projects/Test/OAuth2/OAuth2.csproj +++ b/projects/Test/OAuth2/OAuth2.csproj @@ -11,8 +11,8 @@ ../../rabbit.snk true - latest - 7.0 + true + 7.3 @@ -23,11 +23,11 @@ - - + + - - + + diff --git a/projects/Test/OAuth2/TestOAuth2.cs b/projects/Test/OAuth2/TestOAuth2.cs index a44700356a..6357e173c2 100644 --- a/projects/Test/OAuth2/TestOAuth2.cs +++ b/projects/Test/OAuth2/TestOAuth2.cs @@ -25,35 +25,71 @@ public OAuth2Options(Mode mode) _mode = mode; } - public string Name => _mode switch + public string Name { - Mode.uaa => "uaa", - Mode.keycloak => "keycloak", - _ => throw new InvalidOperationException(), - }; + get + { + switch (_mode) + { + case Mode.uaa: + return "uaa"; + case Mode.keycloak: + return "keycloak"; + default: + throw new InvalidOperationException(); + } + } + } public string ClientId => "producer"; - public string ClientSecret => _mode switch + public string ClientSecret { - Mode.uaa => "producer_secret", - Mode.keycloak => "kbOFBXI9tANgKUq8vXHLhT6YhbivgXxn", - _ => throw new InvalidOperationException(), - }; + get + { + switch (_mode) + { + case Mode.uaa: + return "producer_secret"; + case Mode.keycloak: + return "kbOFBXI9tANgKUq8vXHLhT6YhbivgXxn"; + default: + throw new InvalidOperationException(); + } + } + } - public string Scope => _mode switch + public string Scope { - Mode.uaa => "", - Mode.keycloak => "rabbitmq:configure:*/* rabbitmq:read:*/* rabbitmq:write:*/*", - _ => throw new InvalidOperationException(), - }; + get + { + switch (_mode) + { + case Mode.uaa: + return string.Empty; + case Mode.keycloak: + return "rabbitmq:configure:*/* rabbitmq:read:*/* rabbitmq:write:*/*"; + default: + throw new InvalidOperationException(); + } + } + } - public string TokenEndpoint => _mode switch + public string TokenEndpoint // => _mode switch { - Mode.uaa => "http://localhost:8080/oauth/token", - Mode.keycloak => "http://localhost:8080/realms/test/protocol/openid-connect/token", - _ => throw new InvalidOperationException(), - }; + get + { + switch (_mode) + { + case Mode.uaa: + return "http://localhost:8080/oauth/token"; + case Mode.keycloak: + return "http://localhost:8080/realms/test/protocol/openid-connect/token"; + default: + throw new InvalidOperationException(); + } + } + } public int TokenExpiresInSeconds => 60; } @@ -62,7 +98,7 @@ public class TestOAuth2 : IAsyncLifetime { private const string Exchange = "test_direct"; - private readonly AutoResetEvent _doneEvent = new AutoResetEvent(false); + private readonly SemaphoreSlim _doneEvent = new SemaphoreSlim(0, 1); private readonly ITestOutputHelper _testOutputHelper; private readonly IConnectionFactory _connectionFactory; private IConnection _connection; @@ -95,38 +131,49 @@ public async Task InitializeAsync() public async Task DisposeAsync() { - await _connection.CloseAsync(); - _connection.Dispose(); + try + { + await _connection.CloseAsync(); + } + finally + { + _doneEvent.Dispose(); + _connection.Dispose(); + } } [Fact] public async void IntegrationTest() { using (IChannel publishChannel = await DeclarePublisherAsync()) - using (IChannel consumeChannel = await DeclareConsumerAsync()) { - await PublishAsync(publishChannel); - Consume(consumeChannel); - - if (_tokenExpiresInSeconds > 0) + using (IChannel consumeChannel = await DeclareConsumerAsync()) { - for (int i = 0; i < 4; i++) - { - _testOutputHelper.WriteLine("Wait until Token expires. Attempt #" + (i + 1)); + await PublishAsync(publishChannel); + await ConsumeAsync(consumeChannel); - await Task.Delay(TimeSpan.FromSeconds(_tokenExpiresInSeconds + 10)); - _testOutputHelper.WriteLine("Resuming .."); + if (_tokenExpiresInSeconds > 0) + { + for (int i = 0; i < 4; i++) + { + _testOutputHelper.WriteLine("Wait until Token expires. Attempt #" + (i + 1)); - await PublishAsync(publishChannel); - _doneEvent.Reset(); + await Task.Delay(TimeSpan.FromSeconds(_tokenExpiresInSeconds + 10)); + _testOutputHelper.WriteLine("Resuming .."); - Consume(consumeChannel); + await PublishAsync(publishChannel); + await ConsumeAsync(consumeChannel); + } } + else + { + Assert.Fail("_tokenExpiresInSeconds is NOT greater than 0"); + } + + await consumeChannel.CloseAsync(); } - else - { - Assert.Fail("_tokenExpiresInSeconds is NOT greater than 0"); - } + + await publishChannel.CloseAsync(); } } @@ -134,7 +181,8 @@ public async void IntegrationTest() public async void SecondConnectionCrashes_GH1429() { // https://github.com/rabbitmq/rabbitmq-dotnet-client/issues/1429 - using IConnection secondConnection = await _connectionFactory.CreateConnectionAsync(CancellationToken.None); + IConnection secondConnection = await _connectionFactory.CreateConnectionAsync(CancellationToken.None); + secondConnection.Dispose(); } private async Task DeclarePublisherAsync() @@ -164,20 +212,20 @@ private async Task PublishAsync(IChannel publisher) private async ValueTask DeclareConsumerAsync() { - IChannel subscriber = _connection.CreateChannel(); - await subscriber.QueueDeclareAsync(queue: "testqueue", passive: false, true, false, false, arguments: null); - subscriber.QueueBind("testqueue", Exchange, "hello"); + IChannel subscriber = await _connection.CreateChannelAsync(); + await subscriber.QueueDeclareAsync(queue: "testqueue", true, false, false); + await subscriber.QueueBindAsync("testqueue", Exchange, "hello"); return subscriber; } - private void Consume(IChannel subscriber) + private async Task ConsumeAsync(IChannel subscriber) { var asyncListener = new AsyncEventingBasicConsumer(subscriber); asyncListener.Received += AsyncListener_Received; - string consumerTag = subscriber.BasicConsume("testqueue", true, "testconsumer", asyncListener); - _doneEvent.WaitOne(1); + string consumerTag = await subscriber.BasicConsumeAsync("testqueue", true, "testconsumer", asyncListener); + await _doneEvent.WaitAsync(TimeSpan.FromMilliseconds(500)); _testOutputHelper.WriteLine("Received message"); - subscriber.BasicCancel(consumerTag); + await subscriber.BasicCancelAsync(consumerTag); } private OAuth2ClientCredentialsProvider GetCredentialsProvider(OAuth2Options opts) @@ -195,7 +243,7 @@ private OAuth2ClientCredentialsProvider GetCredentialsProvider(OAuth2Options opt private Task AsyncListener_Received(object sender, BasicDeliverEventArgs @event) { - _doneEvent.Set(); + _doneEvent.Release(); return Task.CompletedTask; } diff --git a/projects/Test/SequentialIntegration/SequentialIntegration.csproj b/projects/Test/SequentialIntegration/SequentialIntegration.csproj index 2820689e79..1020dddbbd 100644 --- a/projects/Test/SequentialIntegration/SequentialIntegration.csproj +++ b/projects/Test/SequentialIntegration/SequentialIntegration.csproj @@ -11,9 +11,8 @@ ../../rabbit.snk true - latest - 7.0 true + 7.3 @@ -37,8 +36,8 @@ - - + + diff --git a/projects/Test/SequentialIntegration/SequentialIntegrationFixture.cs b/projects/Test/SequentialIntegration/SequentialIntegrationFixture.cs index 4f8dd82a3e..960ac0d6ad 100644 --- a/projects/Test/SequentialIntegration/SequentialIntegrationFixture.cs +++ b/projects/Test/SequentialIntegration/SequentialIntegrationFixture.cs @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------- using System; -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using Xunit.Abstractions; @@ -42,48 +42,40 @@ public SequentialIntegrationFixture(ITestOutputHelper output) : base(output) { } - public void Block() + public async Task BlockAsync(IChannel channel) { - _rabbitMQCtl.ExecRabbitMQCtl("set_vm_memory_high_watermark 0.000000001"); + await _rabbitMQCtl.ExecRabbitMQCtlAsync("set_vm_memory_high_watermark 0.000000001"); // give rabbitmqctl some time to do its job - Thread.Sleep(TimeSpan.FromSeconds(2)); - Publish(); + await Task.Delay(TimeSpan.FromSeconds(5)); + await channel.BasicPublishAsync("amq.fanout", "", _encoding.GetBytes("message")); } - public void Unblock() + public Task UnblockAsync() { - _rabbitMQCtl.ExecRabbitMQCtl("set_vm_memory_high_watermark 0.4"); + return _rabbitMQCtl.ExecRabbitMQCtlAsync("set_vm_memory_high_watermark 0.4"); } - public void RestartRabbitMQ() + public async Task RestartRabbitMqAsync() { - StopRabbitMQ(); - Thread.Sleep(TimeSpan.FromMilliseconds(500)); - StartRabbitMQ(); - AwaitRabbitMQ(); + await StopRabbitMqAsync(); + await Task.Delay(TimeSpan.FromSeconds(1)); + await StartRabbitMqAsync(); + await AwaitRabbitMqAsync(); } - public void StopRabbitMQ() + public Task StopRabbitMqAsync() { - _rabbitMQCtl.ExecRabbitMQCtl("stop_app"); + return _rabbitMQCtl.ExecRabbitMQCtlAsync("stop_app"); } - public void StartRabbitMQ() + public Task StartRabbitMqAsync() { - _rabbitMQCtl.ExecRabbitMQCtl("start_app"); + return _rabbitMQCtl.ExecRabbitMQCtlAsync("start_app"); } - private void AwaitRabbitMQ() + private Task AwaitRabbitMqAsync() { - _rabbitMQCtl.ExecRabbitMQCtl("await_startup"); - } - - private void Publish() - { - using (IChannel ch = _conn.CreateChannel()) - { - ch.BasicPublish("amq.fanout", "", _encoding.GetBytes("message")); - } + return _rabbitMQCtl.ExecRabbitMQCtlAsync("await_startup"); } } } diff --git a/projects/Test/SequentialIntegration/TestConnectionBlocked.cs b/projects/Test/SequentialIntegration/TestConnectionBlocked.cs index 232a83b954..f9e3eaf2d4 100644 --- a/projects/Test/SequentialIntegration/TestConnectionBlocked.cs +++ b/projects/Test/SequentialIntegration/TestConnectionBlocked.cs @@ -30,8 +30,8 @@ //--------------------------------------------------------------------------- using System; -using System.Threading; using System.Threading.Tasks; +using RabbitMQ.Client; using RabbitMQ.Client.Events; using Xunit; using Xunit.Abstractions; @@ -40,89 +40,54 @@ namespace Test.SequentialIntegration { public class TestConnectionBlocked : SequentialIntegrationFixture { - private readonly ManualResetEventSlim _connDisposed = new ManualResetEventSlim(false); - private readonly object _lockObject = new object(); - private bool _notified; - public TestConnectionBlocked(ITestOutputHelper output) : base(output) { } - [Fact] - public void TestConnectionBlockedNotification() + public override async Task DisposeAsync() { - _notified = false; - _conn.ConnectionBlocked += HandleBlocked; - _conn.ConnectionUnblocked += HandleUnblocked; - - try - { - Block(); - - lock (_lockObject) - { - if (!_notified) - { - Monitor.Wait(_lockObject, TimeSpan.FromSeconds(15)); - } - } - - if (!_notified) - { - Assert.Fail("Unblock notification not received."); - } - } - finally - { - Unblock(); - } + await UnblockAsync(); + await base.DisposeAsync(); } [Fact] - public void TestDisposeOnBlockedConnectionDoesNotHang() + public async Task TestConnectionBlockedNotification() { - _notified = false; - - try + var tcs = new TaskCompletionSource(); + _conn.ConnectionBlocked += (object sender, ConnectionBlockedEventArgs args) => { - Block(); - - Task.Factory.StartNew(DisposeConnection); + UnblockAsync(); + }; - if (!_connDisposed.Wait(TimeSpan.FromSeconds(20))) - { - Assert.Fail("Dispose must have finished within 20 seconds after starting"); - } - } - finally + _conn.ConnectionUnblocked += (object sender, EventArgs ea) => { - Unblock(); - } - } + tcs.SetResult(true); + }; - protected override void TearDown() - { - Unblock(); + await BlockAsync(_channel); + await tcs.Task.WaitAsync(TimeSpan.FromSeconds(15)); + Assert.True(await tcs.Task, "Unblock notification not received."); } - private void HandleBlocked(object sender, ConnectionBlockedEventArgs args) + [Fact] + public async Task TestDisposeOnBlockedConnectionDoesNotHang() { - Unblock(); - } + var tcs = new TaskCompletionSource(); - private void HandleUnblocked(object sender, EventArgs ea) - { - lock (_lockObject) + await BlockAsync(_channel); + + Task disposeTask = Task.Run(async () => { - _notified = true; - Monitor.PulseAll(_lockObject); - } - } + await _conn.AbortAsync(); + _conn.Dispose(); + _conn = null; + tcs.SetResult(true); + }); - private void DisposeConnection() - { - _conn.Dispose(); - _connDisposed.Set(); + Task anyTask = Task.WhenAny(tcs.Task, disposeTask); + await anyTask.WaitAsync(LongWaitSpan); + bool disposeSuccess = await tcs.Task; + Assert.True(disposeSuccess, "Dispose must have finished within 20 seconds after starting"); } } } diff --git a/projects/Test/SequentialIntegration/TestConnectionRecovery.cs b/projects/Test/SequentialIntegration/TestConnectionRecovery.cs index a2abcb0a1e..a31ac8603d 100644 --- a/projects/Test/SequentialIntegration/TestConnectionRecovery.cs +++ b/projects/Test/SequentialIntegration/TestConnectionRecovery.cs @@ -40,6 +40,7 @@ using RabbitMQ.Client.Impl; using Xunit; using Xunit.Abstractions; +using QueueDeclareOk = RabbitMQ.Client.QueueDeclareOk; namespace Test.SequentialIntegration { @@ -52,145 +53,158 @@ public TestConnectionRecovery(ITestOutputHelper output) : base(output) _queueName = $"{nameof(TestConnectionRecovery)}-{Guid.NewGuid()}"; } - protected override void TearDown() + public override async Task DisposeAsync() { - var cf = CreateConnectionFactory(); - cf.ClientProvidedName = cf.ClientProvidedName + "-TearDown"; - using IConnection conn = cf.CreateConnection(); - using IChannel ch = conn.CreateChannel(); - ch.QueueDelete(_queueName); - base.TearDown(); + ConnectionFactory cf = CreateConnectionFactory(); + cf.ClientProvidedName += "-TearDown"; + using (IConnection conn = await cf.CreateConnectionAsync()) + { + using (IChannel ch = await conn.CreateChannelAsync()) + { + await ch.QueueDeleteAsync(_queueName); + await ch.CloseAsync(); + } + await conn.CloseAsync(); + } + + await base.DisposeAsync(); } [Fact] - public void TestBasicAckAfterChannelRecovery() + public async Task TestBasicAckAfterChannelRecovery() { - var allMessagesSeenLatch = new ManualResetEventSlim(false); - var cons = new AckingBasicConsumer(_channel, _totalMessageCount, allMessagesSeenLatch); + var allMessagesSeenTcs = new TaskCompletionSource(); + var cons = new AckingBasicConsumer(_channel, _totalMessageCount, allMessagesSeenTcs); - string queueName = _channel.QueueDeclare(_queueName, false, false, false, null).QueueName; + QueueDeclareOk q = await _channel.QueueDeclareAsync(_queueName, false, false, false); + string queueName = q.QueueName; Assert.Equal(queueName, _queueName); - _channel.BasicQos(0, 1, false); - _channel.BasicConsume(queueName, false, cons); + await _channel.BasicQosAsync(0, 1, false); + await _channel.BasicConsumeAsync(queueName, false, cons); - ManualResetEventSlim sl = PrepareForShutdown(_conn); - ManualResetEventSlim rl = PrepareForRecovery(_conn); + TaskCompletionSource sl = PrepareForShutdown(_conn); + TaskCompletionSource rl = PrepareForRecovery(_conn); - PublishMessagesWhileClosingConn(queueName); + await PublishMessagesWhileClosingConnAsync(queueName); - Wait(sl, "connection shutdown"); - Wait(rl, "connection recovery"); - Wait(allMessagesSeenLatch, "all messages seen"); + await WaitAsync(sl, "connection shutdown"); + await WaitAsync(rl, "connection recovery"); + await WaitAsync(allMessagesSeenTcs, "all messages seen"); } [Fact] - public void TestBasicNackAfterChannelRecovery() + public async Task TestBasicNackAfterChannelRecovery() { - var allMessagesSeenLatch = new ManualResetEventSlim(false); - var cons = new NackingBasicConsumer(_channel, _totalMessageCount, allMessagesSeenLatch); + var allMessagesSeenTcs = new TaskCompletionSource(); + var cons = new NackingBasicConsumer(_channel, _totalMessageCount, allMessagesSeenTcs); - string queueName = _channel.QueueDeclare(_queueName, false, false, false, null).QueueName; + QueueDeclareOk q = await _channel.QueueDeclareAsync(_queueName, false, false, false); + string queueName = q.QueueName; Assert.Equal(queueName, _queueName); - _channel.BasicQos(0, 1, false); - _channel.BasicConsume(queueName, false, cons); + await _channel.BasicQosAsync(0, 1, false); + await _channel.BasicConsumeAsync(queueName, false, cons); - ManualResetEventSlim sl = PrepareForShutdown(_conn); - ManualResetEventSlim rl = PrepareForRecovery(_conn); + TaskCompletionSource sl = PrepareForShutdown(_conn); + TaskCompletionSource rl = PrepareForRecovery(_conn); - PublishMessagesWhileClosingConn(queueName); + await PublishMessagesWhileClosingConnAsync(queueName); - Wait(sl, "connection shutdown"); - Wait(rl, "connection recovery"); - Wait(allMessagesSeenLatch, "all messages seen"); + await WaitAsync(sl, "connection shutdown"); + await WaitAsync(rl, "connection recovery"); + await WaitAsync(allMessagesSeenTcs, "all messages seen"); } [Fact] - public void TestBasicRejectAfterChannelRecovery() + public async Task TestBasicRejectAfterChannelRecovery() { - var allMessagesSeenLatch = new ManualResetEventSlim(false); - var cons = new RejectingBasicConsumer(_channel, _totalMessageCount, allMessagesSeenLatch); + var allMessagesSeenTcs = new TaskCompletionSource(); + var cons = new RejectingBasicConsumer(_channel, _totalMessageCount, allMessagesSeenTcs); - string queueName = _channel.QueueDeclare(_queueName, false, false, false, null).QueueName; + string queueName = (await _channel.QueueDeclareAsync(_queueName, false, false, false)).QueueName; Assert.Equal(queueName, _queueName); - _channel.BasicQos(0, 1, false); - _channel.BasicConsume(queueName, false, cons); + await _channel.BasicQosAsync(0, 1, false); + await _channel.BasicConsumeAsync(queueName, false, cons); - ManualResetEventSlim sl = PrepareForShutdown(_conn); - ManualResetEventSlim rl = PrepareForRecovery(_conn); + TaskCompletionSource sl = PrepareForShutdown(_conn); + TaskCompletionSource rl = PrepareForRecovery(_conn); - PublishMessagesWhileClosingConn(queueName); + await PublishMessagesWhileClosingConnAsync(queueName); - Wait(sl, "connection shutdown"); - Wait(rl, "connection recovery"); - Wait(allMessagesSeenLatch, "all messages seen"); + await WaitAsync(sl, "connection shutdown"); + await WaitAsync(rl, "connection recovery"); + await WaitAsync(allMessagesSeenTcs, "all messages seen"); } [Fact] - public void TestBasicAckAfterBasicGetAndChannelRecovery() + public async Task TestBasicAckAfterBasicGetAndChannelRecovery() { string q = GenerateQueueName(); - _channel.QueueDeclare(q, false, false, false, null); + await _channel.QueueDeclareAsync(q, false, false, false); // create an offset - _channel.BasicPublish("", q, _messageBody); - Thread.Sleep(50); - BasicGetResult g = _channel.BasicGet(q, false); - CloseAndWaitForRecovery(); + await _channel.BasicPublishAsync("", q, _messageBody); + await Task.Delay(50); + BasicGetResult g = await _channel.BasicGetAsync(q, false); + await CloseAndWaitForRecoveryAsync(); Assert.True(_conn.IsOpen); Assert.True(_channel.IsOpen); // ack the message after recovery - this should be out of range and ignored - _channel.BasicAck(g.DeliveryTag, false); + await _channel.BasicAckAsync(g.DeliveryTag, false); // do a sync operation to 'check' there is no channel exception - _channel.BasicGet(q, false); + await _channel.BasicGetAsync(q, false); } [Fact] - public void TestBasicAckEventHandlerRecovery() + public async Task TestBasicAckEventHandlerRecovery() { - _channel.ConfirmSelect(); - var latch = new ManualResetEventSlim(false); - ((AutorecoveringChannel)_channel).BasicAcks += (m, args) => latch.Set(); - ((AutorecoveringChannel)_channel).BasicNacks += (m, args) => latch.Set(); + await _channel.ConfirmSelectAsync(); + var tcs = new TaskCompletionSource(); + ((AutorecoveringChannel)_channel).BasicAcks += (m, args) => tcs.SetResult(true); + ((AutorecoveringChannel)_channel).BasicNacks += (m, args) => tcs.SetResult(true); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); - WithTemporaryNonExclusiveQueue(_channel, (m, q) => m.BasicPublish("", q, _messageBody)); - Wait(latch, "basic acks/nacks"); + await WithTemporaryNonExclusiveQueueAsync(_channel, (ch, q) => + { + return ch.BasicPublishAsync("", q, _messageBody).AsTask(); + }); + + await WaitAsync(tcs, "basic acks/nacks"); } [Fact] - public void TestBasicConnectionRecovery() + public async Task TestBasicConnectionRecovery() { Assert.True(_conn.IsOpen); - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_conn.IsOpen); } [Fact] - public void TestBasicConnectionRecoveryOnBrokerRestart() + public async Task BasicConnectionRecoveryOnBrokerRestart() { Assert.True(_conn.IsOpen); - RestartServerAndWaitForRecovery(); + await RestartServerAndWaitForRecoveryAsync(); Assert.True(_conn.IsOpen); } [Fact] - public void TestBasicChannelRecovery() + public async Task TestBasicChannelRecovery() { Assert.True(_channel.IsOpen); - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); } [Fact] - public void TestBasicChannelRecoveryOnServerRestart() + public async Task TestBasicChannelRecoveryOnServerRestart() { Assert.True(_channel.IsOpen); - RestartServerAndWaitForRecovery(); + await RestartServerAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); } @@ -215,7 +229,7 @@ void _channel_ChannelShutdown(object sender, ShutdownEventArgs e) byte[] body = GetRandomBody(64); - RestartServerAndWaitForRecovery(); + await RestartServerAndWaitForRecoveryAsync(); Task publishTask = Task.Run(async () => { @@ -242,307 +256,323 @@ void _channel_ChannelShutdown(object sender, ShutdownEventArgs e) // This is false because the channel has been recovered Assert.False(_channel.IsClosed); + await _channel.CloseAsync(); _channel.Dispose(); Assert.True(_channel.IsClosed); + _channel = null; await publishTask; } [Fact] - public void TestBlockedListenersRecovery() + public async Task TestBlockedListenersRecovery() { - var latch = new ManualResetEventSlim(false); - _conn.ConnectionBlocked += (c, reason) => latch.Set(); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); - - Block(); - Wait(latch, "connection blocked"); - - Unblock(); + try + { + var tcs = new TaskCompletionSource(); + _conn.ConnectionBlocked += (c, reason) => tcs.SetResult(true); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); + await BlockAsync(_channel); + await WaitAsync(tcs, "connection blocked"); + } + finally + { + await UnblockAsync(); + } } [Fact] - public void TestClientNamedQueueRecovery() + public Task TestClientNamedQueueRecovery() { string s = "dotnet-client.test.recovery.q1"; - WithTemporaryNonExclusiveQueue(_channel, (m, q) => + return WithTemporaryNonExclusiveQueueAsync(_channel, async (m, q) => { - CloseAndWaitForRecovery(); - AssertQueueRecovery(m, q, false); - _channel.QueueDelete(q); + await CloseAndWaitForRecoveryAsync(); + await AssertQueueRecoveryAsync(m, q, false); + await _channel.QueueDeleteAsync(q); }, s); } [Fact] - public void TestClientNamedQueueRecoveryNoWait() + public Task TestClientNamedQueueRecoveryNoWait() { string s = "dotnet-client.test.recovery.q1-nowait"; - WithTemporaryQueueNoWait(_channel, (m, q) => + return WithTemporaryExclusiveQueueNoWaitAsync(_channel, async (ch, q) => { - CloseAndWaitForRecovery(); - AssertQueueRecovery(m, q); + await CloseAndWaitForRecoveryAsync(); + await AssertExclusiveQueueRecoveryAsync(ch, q); }, s); } [Fact] - public void TestClientNamedQueueRecoveryOnServerRestart() + public Task TestClientNamedQueueRecoveryOnServerRestart() { string s = "dotnet-client.test.recovery.q1"; - WithTemporaryNonExclusiveQueue(_channel, (m, q) => + return WithTemporaryNonExclusiveQueueAsync(_channel, async (m, q) => { - RestartServerAndWaitForRecovery(); - AssertQueueRecovery(m, q, false); - _channel.QueueDelete(q); + await RestartServerAndWaitForRecoveryAsync(); + await AssertQueueRecoveryAsync(m, q, false); + await _channel.QueueDeleteAsync(q); }, s); } [Fact] - public void TestConsumerRecoveryWithManyConsumers() + public async Task TestConsumerRecoveryWithManyConsumers() { - string q = _channel.QueueDeclare(GenerateQueueName(), false, false, false, null).QueueName; + string q = (await _channel.QueueDeclareAsync(GenerateQueueName(), false, false, false)).QueueName; int n = 1024; for (int i = 0; i < n; i++) { var cons = new EventingBasicConsumer(_channel); - _channel.BasicConsume(q, true, cons); + await _channel.BasicConsumeAsync(q, true, cons); } - var latch = new ManualResetEventSlim(false); - ((AutorecoveringConnection)_conn).ConsumerTagChangeAfterRecovery += (prev, current) => latch.Set(); + var tcs = new TaskCompletionSource(); + ((AutorecoveringConnection)_conn).ConsumerTagChangeAfterRecovery += (prev, current) => tcs.SetResult(true); - CloseAndWaitForRecovery(); - Wait(latch, "consumer tag change after recovery"); + await CloseAndWaitForRecoveryAsync(); + await WaitAsync(tcs, "consumer tag change after recovery"); Assert.True(_channel.IsOpen); - AssertConsumerCount(q, n); + await AssertConsumerCountAsync(q, n); } [Fact] - public void TestDeclarationOfManyAutoDeleteExchangesWithTransientExchangesThatAreDeleted() + public async Task TestDeclarationOfManyAutoDeleteExchangesWithTransientExchangesThatAreDeleted() { AssertRecordedExchanges((AutorecoveringConnection)_conn, 0); for (int i = 0; i < 3; i++) { string x1 = $"source-{Guid.NewGuid()}"; - _channel.ExchangeDeclare(x1, "fanout", false, true, null); + await _channel.ExchangeDeclareAsync(x1, "fanout", false, true); + string x2 = $"destination-{Guid.NewGuid()}"; - _channel.ExchangeDeclare(x2, "fanout", false, false, null); - _channel.ExchangeBind(x2, x1, ""); - _channel.ExchangeDelete(x2); + await _channel.ExchangeDeclareAsync(x2, "fanout", false, false); + + await _channel.ExchangeBindAsync(x2, x1, ""); + await _channel.ExchangeDeleteAsync(x2); } AssertRecordedExchanges((AutorecoveringConnection)_conn, 0); } [Fact] - public void TestDeclarationOfManyAutoDeleteExchangesWithTransientExchangesThatAreUnbound() + public async Task TestDeclarationOfManyAutoDeleteExchangesWithTransientExchangesThatAreUnbound() { AssertRecordedExchanges((AutorecoveringConnection)_conn, 0); for (int i = 0; i < 1000; i++) { string x1 = $"source-{Guid.NewGuid()}"; - _channel.ExchangeDeclare(x1, "fanout", false, true, null); + await _channel.ExchangeDeclareAsync(x1, "fanout", false, true); string x2 = $"destination-{Guid.NewGuid()}"; - _channel.ExchangeDeclare(x2, "fanout", false, false, null); - _channel.ExchangeBind(x2, x1, ""); - _channel.ExchangeUnbind(x2, x1, ""); - _channel.ExchangeDelete(x2); + await _channel.ExchangeDeclareAsync(x2, "fanout", false, false); + await _channel.ExchangeBindAsync(x2, x1, ""); + await _channel.ExchangeUnbindAsync(x2, x1, ""); + await _channel.ExchangeDeleteAsync(x2); } AssertRecordedExchanges((AutorecoveringConnection)_conn, 0); } [Fact] - public void TestDeclarationOfManyAutoDeleteExchangesWithTransientQueuesThatAreDeleted() + public async Task TestDeclarationOfManyAutoDeleteExchangesWithTransientQueuesThatAreDeleted() { AssertRecordedExchanges((AutorecoveringConnection)_conn, 0); for (int i = 0; i < 1000; i++) { string x = Guid.NewGuid().ToString(); - _channel.ExchangeDeclare(x, "fanout", false, true, null); - RabbitMQ.Client.QueueDeclareOk q = _channel.QueueDeclare(); - _channel.QueueBind(q, x, ""); - _channel.QueueDelete(q); + await _channel.ExchangeDeclareAsync(x, "fanout", false, true); + RabbitMQ.Client.QueueDeclareOk q = await _channel.QueueDeclareAsync(); + await _channel.QueueBindAsync(q, x, ""); + await _channel.QueueDeleteAsync(q); } AssertRecordedExchanges((AutorecoveringConnection)_conn, 0); } [Fact] - public void TestDeclarationOfManyAutoDeleteExchangesWithTransientQueuesThatAreUnbound() + public async Task TestDeclarationOfManyAutoDeleteExchangesWithTransientQueuesThatAreUnbound() { AssertRecordedExchanges((AutorecoveringConnection)_conn, 0); for (int i = 0; i < 1000; i++) { string x = Guid.NewGuid().ToString(); - _channel.ExchangeDeclare(x, "fanout", false, true, null); - RabbitMQ.Client.QueueDeclareOk q = _channel.QueueDeclare(); - _channel.QueueBind(q, x, ""); - _channel.QueueUnbind(q, x, "", null); + await _channel.ExchangeDeclareAsync(x, "fanout", false, true); + RabbitMQ.Client.QueueDeclareOk q = await _channel.QueueDeclareAsync(); + await _channel.QueueBindAsync(q, x, ""); + await _channel.QueueUnbindAsync(q, x, "", null); } AssertRecordedExchanges((AutorecoveringConnection)_conn, 0); } [Fact] - public void TestDeclarationOfManyAutoDeleteQueuesWithTransientConsumer() + public async Task TestDeclarationOfManyAutoDeleteQueuesWithTransientConsumer() { AssertRecordedQueues((AutorecoveringConnection)_conn, 0); for (int i = 0; i < 1000; i++) { string q = Guid.NewGuid().ToString(); - _channel.QueueDeclare(q, false, false, true, null); + await _channel.QueueDeclareAsync(q, false, false, true); var dummy = new EventingBasicConsumer(_channel); - string tag = _channel.BasicConsume(q, true, dummy); - _channel.BasicCancel(tag); + string tag = await _channel.BasicConsumeAsync(q, true, dummy); + await _channel.BasicCancelAsync(tag); } AssertRecordedQueues((AutorecoveringConnection)_conn, 0); } [Fact] - public void TestExchangeRecovery() + public async Task TestExchangeRecovery() { string x = "dotnet-client.test.recovery.x1"; - DeclareNonDurableExchange(_channel, x); - CloseAndWaitForRecovery(); - AssertExchangeRecovery(_channel, x); - _channel.ExchangeDelete(x); + await DeclareNonDurableExchangeAsync(_channel, x); + await CloseAndWaitForRecoveryAsync(); + await AssertExchangeRecoveryAsync(_channel, x); + await _channel.ExchangeDeleteAsync(x); } [Fact] - public void TestExchangeRecoveryWithNoWait() + public async Task TestExchangeToExchangeBindingRecovery() { - string x = "dotnet-client.test.recovery.x1-nowait"; - DeclareNonDurableExchangeNoWait(_channel, x); - CloseAndWaitForRecovery(); - AssertExchangeRecovery(_channel, x); - _channel.ExchangeDelete(x); - } - - [Fact] - public void TestExchangeToExchangeBindingRecovery() - { - string q = _channel.QueueDeclare("", false, false, false, null).QueueName; + string q = (await _channel.QueueDeclareAsync("", false, false, false)).QueueName; string x1 = "amq.fanout"; string x2 = GenerateExchangeName(); - _channel.ExchangeDeclare(x2, "fanout"); - _channel.ExchangeBind(x1, x2, ""); - _channel.QueueBind(q, x1, ""); + await _channel.ExchangeDeclareAsync(x2, "fanout"); + await _channel.ExchangeBindAsync(x1, x2, ""); + await _channel.QueueBindAsync(q, x1, ""); try { - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); - _channel.BasicPublish(x2, "", _encoding.GetBytes("msg")); - AssertMessageCount(q, 1); + await _channel.BasicPublishAsync(x2, "", _encoding.GetBytes("msg")); + await AssertMessageCountAsync(q, 1); } finally { - WithTemporaryChannel(m => + await WithTemporaryChannelAsync(async ch => { - m.ExchangeDelete(x2); - m.QueueDelete(q); + await ch.ExchangeDeleteAsync(x2); + await ch.QueueDeleteAsync(q); }); } } [Fact] - public void TestQueueRecoveryWithManyQueues() + public async Task TestQueueRecoveryWithManyQueues() { var qs = new List(); int n = 1024; for (int i = 0; i < n; i++) { - qs.Add(_channel.QueueDeclare(GenerateQueueName(), false, false, false, null).QueueName); + QueueDeclareOk q = await _channel.QueueDeclareAsync(GenerateQueueName(), false, false, false); + qs.Add(q.QueueName); } - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); foreach (string q in qs) { - AssertQueueRecovery(_channel, q, false); - _channel.QueueDelete(q); + await AssertQueueRecoveryAsync(_channel, q, false); + await _channel.QueueDeleteAsync(q); } } // rabbitmq/rabbitmq-dotnet-client#43 [Fact] - public void TestClientNamedTransientAutoDeleteQueueAndBindingRecovery() + public async Task TestClientNamedTransientAutoDeleteQueueAndBindingRecovery() { - string q = Guid.NewGuid().ToString(); - string x = "tmp-fanout"; - IChannel ch = _conn.CreateChannel(); - ch.QueueDelete(q); - ch.ExchangeDelete(x); - ch.ExchangeDeclare(exchange: x, type: "fanout"); - ch.QueueDeclare(queue: q, durable: false, exclusive: false, autoDelete: true, arguments: null); - ch.QueueBind(queue: q, exchange: x, routingKey: ""); - RestartServerAndWaitForRecovery(); - Assert.True(ch.IsOpen); - ch.ConfirmSelect(); - ch.QueuePurge(q); - ch.ExchangeDeclare(exchange: x, type: "fanout"); - ch.BasicPublish(exchange: x, routingKey: "", body: _encoding.GetBytes("msg")); - WaitForConfirms(ch); - RabbitMQ.Client.QueueDeclareOk ok = ch.QueueDeclare(queue: q, durable: false, exclusive: false, autoDelete: true, arguments: null); - Assert.Equal(1u, ok.MessageCount); - ch.QueueDelete(q); - ch.ExchangeDelete(x); + string queueName = GenerateQueueName(); + string exchangeName = GenerateExchangeName(); + try + { + await _channel.QueueDeleteAsync(queueName); + await _channel.ExchangeDeleteAsync(exchangeName); + + await _channel.ExchangeDeclareAsync(exchange: exchangeName, type: "fanout"); + await _channel.QueueDeclareAsync(queue: queueName, durable: false, exclusive: false, autoDelete: true, arguments: null); + await _channel.QueueBindAsync(queue: queueName, exchange: exchangeName, routingKey: ""); + + await RestartServerAndWaitForRecoveryAsync(); + Assert.True(_channel.IsOpen); + + await _channel.ConfirmSelectAsync(); + QueueDeclareOk ok0 = await _channel.QueueDeclarePassiveAsync(queue: queueName); + Assert.Equal(queueName, ok0.QueueName); + await _channel.QueuePurgeAsync(queueName); + await _channel.ExchangeDeclarePassiveAsync(exchange: exchangeName); + await _channel.BasicPublishAsync(exchange: exchangeName, routingKey: "", body: _encoding.GetBytes("msg")); + + await WaitForConfirmsWithCancellationAsync(_channel); + + QueueDeclareOk ok1 = await _channel.QueueDeclarePassiveAsync(queue: queueName); + Assert.Equal(1u, ok1.MessageCount); + } + finally + { + await _channel.QueueDeleteAsync(queueName); + await _channel.ExchangeDeleteAsync(exchangeName); + } } // rabbitmq/rabbitmq-dotnet-client#43 [Fact] - public void TestServerNamedTransientAutoDeleteQueueAndBindingRecovery() + public async Task TestServerNamedTransientAutoDeleteQueueAndBindingRecovery() { string x = "tmp-fanout"; - _channel.ExchangeDelete(x); - _channel.ExchangeDeclare(exchange: x, type: "fanout"); - string q = _channel.QueueDeclare(queue: "", durable: false, exclusive: false, autoDelete: true, arguments: null).QueueName; + await _channel.ExchangeDeleteAsync(x); + await _channel.ExchangeDeclareAsync(exchange: x, type: "fanout"); + string q = (await _channel.QueueDeclareAsync(queue: "", durable: false, exclusive: false, autoDelete: true, arguments: null)).QueueName; string nameBefore = q; string nameAfter = null; - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); + ((AutorecoveringConnection)_conn).QueueNameChangedAfterRecovery += (source, ea) => { nameBefore = ea.NameBefore; nameAfter = ea.NameAfter; - latch.Set(); + tcs.SetResult(true); }; - _channel.QueueBind(queue: nameBefore, exchange: x, routingKey: ""); - RestartServerAndWaitForRecovery(); - Wait(latch, "queue name change after recovery"); + + await _channel.QueueBindAsync(queue: nameBefore, exchange: x, routingKey: ""); + await RestartServerAndWaitForRecoveryAsync(); + + await WaitAsync(tcs, "queue name change after recovery"); Assert.True(_channel.IsOpen); Assert.NotEqual(nameBefore, nameAfter); - _channel.ConfirmSelect(); - _channel.ExchangeDeclare(exchange: x, type: "fanout"); - _channel.BasicPublish(exchange: x, routingKey: "", body: _encoding.GetBytes("msg")); - WaitForConfirms(_channel); - RabbitMQ.Client.QueueDeclareOk ok = _channel.QueueDeclarePassive(nameAfter); + + await _channel.ConfirmSelectAsync(); + await _channel.ExchangeDeclareAsync(exchange: x, type: "fanout"); + await _channel.BasicPublishAsync(exchange: x, routingKey: "", body: _encoding.GetBytes("msg")); + await WaitForConfirmsWithCancellationAsync(_channel); + + QueueDeclareOk ok = await _channel.QueueDeclarePassiveAsync(nameAfter); Assert.Equal(1u, ok.MessageCount); - _channel.QueueDelete(q); - _channel.ExchangeDelete(x); + await _channel.QueueDeleteAsync(q); + await _channel.ExchangeDeleteAsync(x); } [Fact] - public void TestRecoveryEventHandlersOnConnection() + public async Task TestRecoveryEventHandlersOnConnection() { int counter = 0; ((AutorecoveringConnection)_conn).RecoverySucceeded += (source, ea) => Interlocked.Increment(ref counter); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_conn.IsOpen); Assert.True(counter >= 3); } [Fact] - public void TestRecoveryEventHandlersOnChannel() + public async Task TestRecoveryEventHandlersOnChannel() { int counter = 0; ((AutorecoveringChannel)_channel).Recovery += (source, ea) => Interlocked.Increment(ref counter); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); Assert.True(counter >= 3); } @@ -550,30 +580,30 @@ public void TestRecoveryEventHandlersOnChannel() [Theory] [InlineData(1)] [InlineData(3)] - public void TestRecoveringConsumerHandlerOnConnection(int iterations) + public async Task TestRecoveringConsumerHandlerOnConnection(int iterations) { - string q = _channel.QueueDeclare(GenerateQueueName(), false, false, false, null).QueueName; + string q = (await _channel.QueueDeclareAsync(GenerateQueueName(), false, false, false)).QueueName; var cons = new EventingBasicConsumer(_channel); - _channel.BasicConsume(q, true, cons); + await _channel.BasicConsumeAsync(q, true, cons); int counter = 0; ((AutorecoveringConnection)_conn).RecoveringConsumer += (sender, args) => Interlocked.Increment(ref counter); for (int i = 0; i < iterations; i++) { - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); } Assert.Equal(iterations, counter); } [Fact] - public void TestRecoveringConsumerHandlerOnConnection_EventArgumentsArePassedDown() + public async Task TestRecoveringConsumerHandlerOnConnection_EventArgumentsArePassedDown() { var myArgs = new Dictionary { { "first-argument", "some-value" } }; - string q = _channel.QueueDeclare(GenerateQueueName(), false, false, false, null).QueueName; + string q = (await _channel.QueueDeclareAsync(GenerateQueueName(), false, false, false)).QueueName; var cons = new EventingBasicConsumer(_channel); - string expectedCTag = _channel.BasicConsume(cons, q, arguments: myArgs); + string expectedCTag = await _channel.BasicConsumeAsync(cons, q, arguments: myArgs); bool ctagMatches = false; bool consumerArgumentMatches = false; @@ -587,7 +617,7 @@ public void TestRecoveringConsumerHandlerOnConnection_EventArgumentsArePassedDow args.ConsumerArguments["first-argument"] = "event-handler-set-this-value"; }; - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); Assert.True(ctagMatches, "expected consumer tag to match"); Assert.True(consumerArgumentMatches, "expected consumer arguments to match"); string actualVal = (string)Assert.Contains("first-argument", myArgs as IDictionary); @@ -595,151 +625,152 @@ public void TestRecoveringConsumerHandlerOnConnection_EventArgumentsArePassedDow } [Fact] - public void TestServerNamedQueueRecovery() + public async Task TestServerNamedQueueRecovery() { - string q = _channel.QueueDeclare("", false, false, false, null).QueueName; + string q = (await _channel.QueueDeclareAsync("", false, false, false)).QueueName; string x = "amq.fanout"; - _channel.QueueBind(q, x, ""); + await _channel.QueueBindAsync(q, x, ""); string nameBefore = q; string nameAfter = null; - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); var connection = (AutorecoveringConnection)_conn; - connection.RecoverySucceeded += (source, ea) => latch.Set(); + connection.RecoverySucceeded += (source, ea) => tcs.SetResult(true); connection.QueueNameChangedAfterRecovery += (source, ea) => { nameAfter = ea.NameAfter; }; - CloseAndWaitForRecovery(); - Wait(latch, "recovery succeeded"); + await CloseAndWaitForRecoveryAsync(); + await WaitAsync(tcs, "recovery succeeded"); Assert.NotNull(nameAfter); Assert.StartsWith("amq.", nameBefore); Assert.StartsWith("amq.", nameAfter); Assert.NotEqual(nameBefore, nameAfter); - _channel.QueueDeclarePassive(nameAfter); + await _channel.QueueDeclarePassiveAsync(nameAfter); } [Fact] - public void TestShutdownEventHandlersRecoveryOnConnection() + public async Task TestShutdownEventHandlersRecoveryOnConnection() { int counter = 0; _conn.ConnectionShutdown += (c, args) => Interlocked.Increment(ref counter); Assert.True(_conn.IsOpen); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_conn.IsOpen); Assert.True(counter >= 3); } [Fact] - public void TestShutdownEventHandlersRecoveryOnConnectionAfterDelayedServerRestart() + public async Task TestShutdownEventHandlersRecoveryOnConnectionAfterDelayedServerRestart() { int counter = 0; _conn.ConnectionShutdown += (c, args) => Interlocked.Increment(ref counter); - ManualResetEventSlim shutdownLatch = PrepareForShutdown(_conn); - ManualResetEventSlim recoveryLatch = PrepareForRecovery((AutorecoveringConnection)_conn); + TaskCompletionSource shutdownLatch = PrepareForShutdown(_conn); + TaskCompletionSource recoveryLatch = PrepareForRecovery((AutorecoveringConnection)_conn); Assert.True(_conn.IsOpen); try { - StopRabbitMQ(); - Thread.Sleep(7000); + await StopRabbitMqAsync(); + await Task.Delay(TimeSpan.FromSeconds(7)); } finally { - StartRabbitMQ(); + await StartRabbitMqAsync(); } - Wait(shutdownLatch, WaitSpan, "connection shutdown"); - Wait(recoveryLatch, WaitSpan, "connection recovery"); + await WaitAsync(shutdownLatch, WaitSpan, "connection shutdown"); + await WaitAsync(recoveryLatch, WaitSpan, "connection recovery"); Assert.True(_conn.IsOpen); Assert.True(counter >= 1); } [Fact] - public void TestShutdownEventHandlersRecoveryOnChannel() + public async Task TestShutdownEventHandlersRecoveryOnChannel() { int counter = 0; _channel.ChannelShutdown += (c, args) => Interlocked.Increment(ref counter); Assert.True(_channel.IsOpen); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); Assert.True(counter >= 3); } [Fact] - public void TestRecoverTopologyOnDisposedChannel() + public async Task TestRecoverTopologyOnDisposedChannel() { string x = GenerateExchangeName(); string q = GenerateQueueName(); const string rk = "routing-key"; - using (IChannel ch = _conn.CreateChannel()) + using (IChannel ch = await _conn.CreateChannelAsync()) { - ch.ExchangeDeclare(exchange: x, type: "fanout"); - ch.QueueDeclare(q, false, false, false, null); - ch.QueueBind(q, x, rk); + await ch.ExchangeDeclareAsync(exchange: x, type: "fanout"); + await ch.QueueDeclareAsync(q, false, false, false); + await ch.QueueBindAsync(q, x, rk); + await ch.CloseAsync(); } var cons = new EventingBasicConsumer(_channel); - _channel.BasicConsume(q, true, cons); - AssertConsumerCount(_channel, q, 1); + await _channel.BasicConsumeAsync(q, true, cons); + await AssertConsumerCountAsync(_channel, q, 1); - CloseAndWaitForRecovery(); - AssertConsumerCount(_channel, q, 1); + await CloseAndWaitForRecoveryAsync(); + await AssertConsumerCountAsync(_channel, q, 1); - var latch = new ManualResetEventSlim(false); - cons.Received += (s, args) => latch.Set(); + var tcs = new TaskCompletionSource(); + cons.Received += (s, args) => tcs.SetResult(true); - _channel.BasicPublish("", q, _messageBody); - Wait(latch, "received event"); + await _channel.BasicPublishAsync("", q, _messageBody); + await WaitAsync(tcs, "received event"); - _channel.QueueUnbind(q, x, rk); - _channel.ExchangeDelete(x); - _channel.QueueDelete(q); + await _channel.QueueUnbindAsync(q, x, rk); + await _channel.ExchangeDeleteAsync(x); + await _channel.QueueDeleteAsync(q); } - [Fact(Skip = "TODO-FLAKY")] - public void TestPublishRpcRightAfterReconnect() + [Fact] + public async Task TestPublishRpcRightAfterReconnect() { string testQueueName = $"dotnet-client.test.{nameof(TestPublishRpcRightAfterReconnect)}"; - _channel.QueueDeclare(testQueueName, false, false, false, null); + await _channel.QueueDeclareAsync(testQueueName, false, false, false); var replyConsumer = new EventingBasicConsumer(_channel); - _channel.BasicConsume("amq.rabbitmq.reply-to", true, replyConsumer); + await _channel.BasicConsumeAsync("amq.rabbitmq.reply-to", true, replyConsumer); var properties = new BasicProperties(); properties.ReplyTo = "amq.rabbitmq.reply-to"; TimeSpan doneSpan = TimeSpan.FromMilliseconds(100); - var done = new ManualResetEventSlim(false); - Task.Run(() => + var doneTcs = new TaskCompletionSource(); + Task closeTask = Task.Run(async () => { try { - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); } finally { - done.Set(); + doneTcs.SetResult(true); } }); - while (!done.IsSet) + while (false == doneTcs.Task.IsCompletedSuccessfully()) { try { - _channel.BasicPublish(string.Empty, testQueueName, properties, _messageBody); + await _channel.BasicPublishAsync(string.Empty, testQueueName, properties, _messageBody); } catch (Exception e) { @@ -749,68 +780,77 @@ public void TestPublishRpcRightAfterReconnect() Assert.NotEqual(406, a.ShutdownReason.ReplyCode); } } - done.Wait(doneSpan); + + try + { + await doneTcs.Task.WaitAsync(doneSpan); + } + catch (TimeoutException) + { + } } + + await closeTask; } [Fact] - public void TestThatCancelledConsumerDoesNotReappearOnRecovery() + public async Task TestThatCancelledConsumerDoesNotReappearOnRecovery() { - string q = _channel.QueueDeclare(GenerateQueueName(), false, false, false, null).QueueName; + string q = (await _channel.QueueDeclareAsync(GenerateQueueName(), false, false, false)).QueueName; int n = 1024; for (int i = 0; i < n; i++) { var cons = new EventingBasicConsumer(_channel); - string tag = _channel.BasicConsume(q, true, cons); - _channel.BasicCancel(tag); + string tag = await _channel.BasicConsumeAsync(q, true, cons); + await _channel.BasicCancelAsync(tag); } - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); - AssertConsumerCount(q, 0); + await AssertConsumerCountAsync(q, 0); } [Fact] - public void TestThatDeletedExchangeBindingsDontReappearOnRecovery() + public async Task TestThatDeletedExchangeBindingsDontReappearOnRecovery() { - string q = _channel.QueueDeclare("", false, false, false, null).QueueName; + string q = (await _channel.QueueDeclareAsync("", false, false, false)).QueueName; string x1 = "amq.fanout"; string x2 = GenerateExchangeName(); - _channel.ExchangeDeclare(x2, "fanout"); - _channel.ExchangeBind(x1, x2, ""); - _channel.QueueBind(q, x1, ""); - _channel.ExchangeUnbind(x1, x2, "", null); + await _channel.ExchangeDeclareAsync(x2, "fanout"); + await _channel.ExchangeBindAsync(x1, x2, ""); + await _channel.QueueBindAsync(q, x1, ""); + await _channel.ExchangeUnbindAsync(x1, x2, ""); try { - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); - _channel.BasicPublish(x2, "", _encoding.GetBytes("msg")); - AssertMessageCount(q, 0); + await _channel.BasicPublishAsync(x2, "", _encoding.GetBytes("msg")); + await AssertMessageCountAsync(q, 0); } finally { - WithTemporaryChannel(m => + await WithTemporaryChannelAsync(async ch => { - m.ExchangeDelete(x2); - m.QueueDelete(q); + await ch.ExchangeDeleteAsync(x2); + await ch.QueueDeleteAsync(q); }); } } [Fact] - public void TestThatDeletedExchangesDontReappearOnRecovery() + public async Task TestThatDeletedExchangesDontReappearOnRecovery() { string x = GenerateExchangeName(); - _channel.ExchangeDeclare(x, "fanout"); - _channel.ExchangeDelete(x); + await _channel.ExchangeDeclareAsync(x, "fanout"); + await _channel.ExchangeDeleteAsync(x); try { - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); - _channel.ExchangeDeclarePassive(x); + await _channel.ExchangeDeclarePassiveAsync(x); Assert.Fail("Expected an exception"); } catch (OperationInterruptedException e) @@ -821,46 +861,46 @@ public void TestThatDeletedExchangesDontReappearOnRecovery() } [Fact] - public void TestThatDeletedQueueBindingsDontReappearOnRecovery() + public async Task TestThatDeletedQueueBindingsDontReappearOnRecovery() { - string q = _channel.QueueDeclare("", false, false, false, null).QueueName; + string q = (await _channel.QueueDeclareAsync("", false, false, false)).QueueName; string x1 = "amq.fanout"; string x2 = GenerateExchangeName(); - _channel.ExchangeDeclare(x2, "fanout"); - _channel.ExchangeBind(x1, x2, ""); - _channel.QueueBind(q, x1, ""); - _channel.QueueUnbind(q, x1, "", null); + await _channel.ExchangeDeclareAsync(x2, "fanout"); + await _channel.ExchangeBindAsync(x1, x2, ""); + await _channel.QueueBindAsync(q, x1, ""); + await _channel.QueueUnbindAsync(q, x1, "", null); try { - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); - _channel.BasicPublish(x2, "", _encoding.GetBytes("msg")); - AssertMessageCount(q, 0); + await _channel.BasicPublishAsync(x2, "", _encoding.GetBytes("msg")); + await AssertMessageCountAsync(q, 0); } finally { - WithTemporaryChannel(m => + await WithTemporaryChannelAsync(async ch => { - m.ExchangeDelete(x2); - m.QueueDelete(q); + await ch.ExchangeDeleteAsync(x2); + await ch.QueueDeleteAsync(q); }); } } [Fact] - public void TestThatDeletedQueuesDontReappearOnRecovery() + public async Task TestThatDeletedQueuesDontReappearOnRecovery() { string q = "dotnet-client.recovery.q1"; - _channel.QueueDeclare(q, false, false, false, null); - _channel.QueueDelete(q); + await _channel.QueueDeclareAsync(q, false, false, false); + await _channel.QueueDeleteAsync(q); try { - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); Assert.True(_channel.IsOpen); - _channel.QueueDeclarePassive(q); + await _channel.QueueDeclarePassiveAsync(q); Assert.Fail("Expected an exception"); } catch (OperationInterruptedException e) @@ -871,75 +911,78 @@ public void TestThatDeletedQueuesDontReappearOnRecovery() } [Fact] - public void TestUnblockedListenersRecovery() + public async Task TestUnblockedListenersRecovery() { - var latch = new ManualResetEventSlim(false); - _conn.ConnectionUnblocked += (source, ea) => latch.Set(); - CloseAndWaitForRecovery(); - CloseAndWaitForRecovery(); - - Block(); - Unblock(); - Wait(latch, "connection unblocked"); + var tcs = new TaskCompletionSource(); + _conn.ConnectionUnblocked += (source, ea) => tcs.SetResult(true); + await CloseAndWaitForRecoveryAsync(); + await CloseAndWaitForRecoveryAsync(); + await BlockAsync(_channel); + await UnblockAsync(); + await WaitAsync(tcs, "connection unblocked"); } [Fact] - public void TestBindingRecovery_GH1035() + public async Task TestBindingRecovery_GH1035() { const string routingKey = "unused"; byte[] body = GetRandomBody(); - using var receivedMessageEvent = new AutoResetEvent(initialState: false); + var receivedMessageSemaphore = new SemaphoreSlim(0, 1); void MessageReceived(object sender, BasicDeliverEventArgs e) { - receivedMessageEvent.Set(); + receivedMessageSemaphore.Release(); } string exchangeName = $"ex-gh-1035-{Guid.NewGuid()}"; string queueName = $"q-gh-1035-{Guid.NewGuid()}"; - _channel.ExchangeDeclare(exchange: exchangeName, + await _channel.ExchangeDeclareAsync(exchange: exchangeName, type: "fanout", durable: false, autoDelete: true, arguments: null); - RabbitMQ.Client.QueueDeclareOk q0 = _channel.QueueDeclare(queue: queueName, exclusive: true); + QueueDeclareOk q0 = await _channel.QueueDeclareAsync(queue: queueName, exclusive: true); Assert.Equal(queueName, q0); - _channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: routingKey); + await _channel.QueueBindAsync(queue: queueName, exchange: exchangeName, routingKey: routingKey); + await _channel.CloseAsync(); _channel.Dispose(); + _channel = null; - _channel = _conn.CreateChannel(); + _channel = await _conn.CreateChannelAsync(); - _channel.ExchangeDeclare(exchange: exchangeName, + await _channel.ExchangeDeclareAsync(exchange: exchangeName, type: "fanout", durable: false, autoDelete: true, arguments: null); - RabbitMQ.Client.QueueDeclareOk q1 = _channel.QueueDeclare(queue: queueName, exclusive: true); + QueueDeclareOk q1 = await _channel.QueueDeclareAsync(queue: queueName, exclusive: true); Assert.Equal(queueName, q1.QueueName); - _channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: routingKey); + await _channel.QueueBindAsync(queue: queueName, exchange: exchangeName, routingKey: routingKey); var c = new EventingBasicConsumer(_channel); c.Received += MessageReceived; - _channel.BasicConsume(queue: queueName, autoAck: true, consumer: c); + await _channel.BasicConsumeAsync(queue: queueName, autoAck: true, consumer: c); - using (IChannel pubCh = _conn.CreateChannel()) + using (IChannel pubCh = await _conn.CreateChannelAsync()) { - pubCh.BasicPublish(exchange: exchangeName, routingKey: routingKey, body: body); + await pubCh.BasicPublishAsync(exchange: exchangeName, routingKey: routingKey, body: body); + await pubCh.CloseAsync(); } - Assert.True(receivedMessageEvent.WaitOne(WaitSpan)); + Assert.True(await receivedMessageSemaphore.WaitAsync(WaitSpan)); - CloseAndWaitForRecovery(); + await CloseAndWaitForRecoveryAsync(); - using (IChannel pubCh = _conn.CreateChannel()) + using (IChannel pubCh = await _conn.CreateChannelAsync()) { - pubCh.BasicPublish(exchange: exchangeName, routingKey: "unused", body: body); + await pubCh.BasicPublishAsync(exchange: exchangeName, routingKey: "unused", body: body); + await pubCh.CloseAsync(); } - Assert.True(receivedMessageEvent.WaitOne(WaitSpan)); + Assert.True(await receivedMessageSemaphore.WaitAsync(WaitSpan)); } } } diff --git a/projects/Test/SequentialIntegration/TestConnectionRecoveryBase.cs b/projects/Test/SequentialIntegration/TestConnectionRecoveryBase.cs index 117a1717db..5d7d495136 100644 --- a/projects/Test/SequentialIntegration/TestConnectionRecoveryBase.cs +++ b/projects/Test/SequentialIntegration/TestConnectionRecoveryBase.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Framing.Impl; using Xunit; @@ -42,7 +43,7 @@ namespace Test.SequentialIntegration public class TestConnectionRecoveryBase : SequentialIntegrationFixture { protected readonly byte[] _messageBody; - protected const ushort _totalMessageCount = 8192; + protected const ushort _totalMessageCount = 16384; protected const ushort _closeAtCount = 16; public TestConnectionRecoveryBase(ITestOutputHelper output) : base(output) @@ -50,54 +51,60 @@ public TestConnectionRecoveryBase(ITestOutputHelper output) : base(output) _messageBody = GetRandomBody(4096); } - protected override void TearDown() + public override async Task DisposeAsync() { - Unblock(); + await UnblockAsync(); + await base.DisposeAsync(); } - protected void AssertConsumerCount(string q, int count) + protected Task AssertConsumerCountAsync(string q, int count) { - WithTemporaryChannel((m) => + return WithTemporaryChannelAsync(async ch => { - RabbitMQ.Client.QueueDeclareOk ok = m.QueueDeclarePassive(q); + RabbitMQ.Client.QueueDeclareOk ok = await ch.QueueDeclarePassiveAsync(q); Assert.Equal((uint)count, ok.ConsumerCount); }); } - protected void AssertConsumerCount(IChannel m, string q, uint count) + protected async Task AssertConsumerCountAsync(IChannel ch, string q, uint count) { - RabbitMQ.Client.QueueDeclareOk ok = m.QueueDeclarePassive(q); + RabbitMQ.Client.QueueDeclareOk ok = await ch.QueueDeclarePassiveAsync(q); Assert.Equal(count, ok.ConsumerCount); } - protected void AssertExchangeRecovery(IChannel m, string x) + protected async Task AssertExchangeRecoveryAsync(IChannel m, string x) { - m.ConfirmSelect(); - WithTemporaryNonExclusiveQueue(m, (_, q) => + await m.ConfirmSelectAsync(); + await WithTemporaryNonExclusiveQueueAsync(m, async (_, q) => { string rk = "routing-key"; - m.QueueBind(q, x, rk); - m.BasicPublish(x, rk, _messageBody); + await m.QueueBindAsync(q, x, rk); + await m.BasicPublishAsync(x, rk, _messageBody); - Assert.True(WaitForConfirms(m)); - m.ExchangeDeclarePassive(x); + Assert.True(await TestConnectionRecoveryBase.WaitForConfirmsWithCancellationAsync(m)); + await m.ExchangeDeclarePassiveAsync(x); }); } - protected void AssertQueueRecovery(IChannel m, string q) + protected Task AssertExclusiveQueueRecoveryAsync(IChannel m, string q) { - AssertQueueRecovery(m, q, true); + return AssertQueueRecoveryAsync(m, q, true); } - protected void AssertQueueRecovery(IChannel m, string q, bool exclusive, IDictionary arguments = null) + protected async Task AssertQueueRecoveryAsync(IChannel ch, string q, bool exclusive, IDictionary arguments = null) { - m.ConfirmSelect(); - m.QueueDeclarePassive(q); - RabbitMQ.Client.QueueDeclareOk ok1 = m.QueueDeclare(q, false, exclusive, false, arguments); + await ch.ConfirmSelectAsync(); + await ch.QueueDeclareAsync(queue: q, passive: true, durable: false, exclusive: false, autoDelete: false, arguments: null); + + RabbitMQ.Client.QueueDeclareOk ok1 = await ch.QueueDeclareAsync(queue: q, passive: false, + durable: false, exclusive: exclusive, autoDelete: false, arguments: arguments); Assert.Equal(0u, ok1.MessageCount); - m.BasicPublish("", q, _messageBody); - Assert.True(WaitForConfirms(m)); - RabbitMQ.Client.QueueDeclareOk ok2 = m.QueueDeclare(q, false, exclusive, false, arguments); + + await ch.BasicPublishAsync("", q, _messageBody); + Assert.True(await WaitForConfirmsWithCancellationAsync(ch)); + + RabbitMQ.Client.QueueDeclareOk ok2 = await ch.QueueDeclareAsync(queue: q, passive: false, + durable: false, exclusive: exclusive, autoDelete: false, arguments: arguments); Assert.Equal(1u, ok2.MessageCount); } @@ -111,20 +118,21 @@ internal void AssertRecordedQueues(AutorecoveringConnection c, int n) Assert.Equal(n, c.RecordedQueuesCount); } - internal AutorecoveringConnection CreateAutorecoveringConnection() + internal Task CreateAutorecoveringConnectionAsync() { - return CreateAutorecoveringConnection(RecoveryInterval); + return CreateAutorecoveringConnectionAsync(RecoveryInterval); } - internal AutorecoveringConnection CreateAutorecoveringConnection(TimeSpan networkRecoveryInterval) + internal async Task CreateAutorecoveringConnectionAsync(TimeSpan networkRecoveryInterval) { var cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; cf.NetworkRecoveryInterval = networkRecoveryInterval; - return (AutorecoveringConnection)cf.CreateConnection(); + IConnection conn = await cf.CreateConnectionAsync(); + return (AutorecoveringConnection)conn; } - internal AutorecoveringConnection CreateAutorecoveringConnection(IList endpoints) + internal async Task CreateAutorecoveringConnectionAsync(IList endpoints) { var cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; @@ -132,257 +140,282 @@ internal AutorecoveringConnection CreateAutorecoveringConnection(IList CreateAutorecoveringConnectionWithTopologyRecoveryDisabledAsync() { var cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; cf.TopologyRecoveryEnabled = false; cf.NetworkRecoveryInterval = RecoveryInterval; - return (AutorecoveringConnection)cf.CreateConnection(); + IConnection conn = await cf.CreateConnectionAsync(); + return (AutorecoveringConnection)conn; } - internal AutorecoveringConnection CreateAutorecoveringConnectionWithTopologyRecoveryFilter(TopologyRecoveryFilter filter) + internal async Task CreateAutorecoveringConnectionWithTopologyRecoveryFilterAsync(TopologyRecoveryFilter filter) { var cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; cf.TopologyRecoveryEnabled = true; cf.TopologyRecoveryFilter = filter; - return (AutorecoveringConnection)cf.CreateConnection(); + IConnection conn = await cf.CreateConnectionAsync(); + return (AutorecoveringConnection)conn; } - internal AutorecoveringConnection CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(TopologyRecoveryExceptionHandler handler) + internal async Task CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandlerAsync(TopologyRecoveryExceptionHandler handler) { var cf = CreateConnectionFactory(); cf.AutomaticRecoveryEnabled = true; cf.TopologyRecoveryEnabled = true; cf.TopologyRecoveryExceptionHandler = handler; - return (AutorecoveringConnection)cf.CreateConnection(); + IConnection conn = await cf.CreateConnectionAsync(); + return (AutorecoveringConnection)conn; } - protected void CloseConnection(IConnection conn) + protected Task CloseConnectionAsync(IConnection conn) { - _rabbitMQCtl.CloseConnection(conn); + return _rabbitMQCtl.CloseConnectionAsync(conn); } - protected void CloseAndWaitForRecovery() + protected Task CloseAndWaitForRecoveryAsync() { - CloseAndWaitForRecovery((AutorecoveringConnection)_conn); + return CloseAndWaitForRecoveryAsync((AutorecoveringConnection)_conn); } - internal void CloseAndWaitForRecovery(AutorecoveringConnection conn) + internal async Task CloseAndWaitForRecoveryAsync(AutorecoveringConnection conn) { - ManualResetEventSlim sl = PrepareForShutdown(conn); - ManualResetEventSlim rl = PrepareForRecovery(conn); - CloseConnection(conn); - Wait(sl, "connection shutdown"); - Wait(rl, "connection recovery"); + TaskCompletionSource sl = PrepareForShutdown(conn); + TaskCompletionSource rl = PrepareForRecovery(conn); + await CloseConnectionAsync(conn); + await WaitAsync(sl, "connection shutdown"); + await WaitAsync(rl, "connection recovery"); } - internal void CloseAndWaitForShutdown(AutorecoveringConnection conn) + internal async Task CloseAndWaitForShutdownAsync(AutorecoveringConnection conn) { - ManualResetEventSlim sl = PrepareForShutdown(conn); - CloseConnection(conn); - Wait(sl, "connection shutdown"); + TaskCompletionSource sl = PrepareForShutdown(conn); + await CloseConnectionAsync(conn); + await WaitAsync(sl, "connection shutdown"); } - protected string DeclareNonDurableExchange(IChannel m, string x) + protected static async Task DeclareNonDurableExchangeAsync(IChannel ch, string exchangeName) { - m.ExchangeDeclare(x, "fanout", false); - return x; + await ch.ExchangeDeclareAsync(exchangeName, "fanout", false); + return exchangeName; } - protected string DeclareNonDurableExchangeNoWait(IChannel m, string x) + protected async Task PublishMessagesWhileClosingConnAsync(string queueName) { - m.ExchangeDeclareNoWait(x, "fanout", false, false, null); - return x; - } - - protected ManualResetEventSlim PrepareForRecovery(IConnection conn) - { - var latch = new ManualResetEventSlim(false); - - AutorecoveringConnection aconn = conn as AutorecoveringConnection; - aconn.RecoverySucceeded += (source, ea) => latch.Set(); - - return latch; - } - - protected void PublishMessagesWhileClosingConn(string queueName) - { - using (AutorecoveringConnection publishingConn = CreateAutorecoveringConnection()) + using (AutorecoveringConnection publishingConn = await CreateAutorecoveringConnectionAsync()) { - using (IChannel publishingChannel = publishingConn.CreateChannel()) + using (IChannel publishingChannel = await publishingConn.CreateChannelAsync()) { for (ushort i = 0; i < _totalMessageCount; i++) { if (i == _closeAtCount) { - CloseConnection(_conn); + await CloseConnectionAsync(_conn); } - publishingChannel.BasicPublish(string.Empty, queueName, _messageBody); + + await publishingChannel.BasicPublishAsync(string.Empty, queueName, _messageBody); } + + await publishingChannel.CloseAsync(); } } } - protected static ManualResetEventSlim PrepareForShutdown(IConnection conn) + protected static TaskCompletionSource PrepareForShutdown(IConnection conn) + { + var tcs = new TaskCompletionSource(); + + AutorecoveringConnection aconn = conn as AutorecoveringConnection; + aconn.ConnectionShutdown += (c, args) => tcs.SetResult(true); + + return tcs; + } + + protected static TaskCompletionSource PrepareForRecovery(IConnection conn) { - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); AutorecoveringConnection aconn = conn as AutorecoveringConnection; - aconn.ConnectionShutdown += (c, args) => latch.Set(); + aconn.RecoverySucceeded += (source, ea) => tcs.SetResult(true); - return latch; + return tcs; } - protected void RestartServerAndWaitForRecovery() + protected Task RestartServerAndWaitForRecoveryAsync() { - RestartServerAndWaitForRecovery((AutorecoveringConnection)_conn); + return RestartServerAndWaitForRecoveryAsync((AutorecoveringConnection)_conn); } - internal void RestartServerAndWaitForRecovery(AutorecoveringConnection conn) + private async Task RestartServerAndWaitForRecoveryAsync(AutorecoveringConnection conn) { - ManualResetEventSlim sl = PrepareForShutdown(conn); - ManualResetEventSlim rl = PrepareForRecovery(conn); - RestartRabbitMQ(); - Wait(sl, "connection shutdown"); - Wait(rl, "connection recovery"); + TaskCompletionSource sl = PrepareForShutdown(conn); + TaskCompletionSource rl = PrepareForRecovery(conn); + await RestartRabbitMqAsync(); + await WaitAsync(sl, "connection shutdown"); + await WaitAsync(rl, "connection recovery"); } - protected bool WaitForConfirms(IChannel m) + protected static Task WaitForConfirmsWithCancellationAsync(IChannel m) { - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(4)); - return m.WaitForConfirmsAsync(cts.Token).EnsureCompleted(); + using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(4))) + { + return m.WaitForConfirmsAsync(cts.Token); + } } - protected void WaitForRecovery() + protected Task WaitForRecoveryAsync() { - Wait(PrepareForRecovery((AutorecoveringConnection)_conn), "recovery succeded"); + TaskCompletionSource tcs = PrepareForRecovery((AutorecoveringConnection)_conn); + return WaitAsync(tcs, "recovery succeded"); } - internal void WaitForRecovery(AutorecoveringConnection conn) + internal Task WaitForRecoveryAsync(AutorecoveringConnection conn) { - Wait(PrepareForRecovery(conn), "recovery succeeded"); + TaskCompletionSource tcs = PrepareForRecovery(conn); + return WaitAsync(tcs, "recovery succeeded"); } - protected void WaitForShutdown() + protected Task WaitForShutdownAsync() { - Wait(PrepareForShutdown(_conn), "connection shutdown"); + TaskCompletionSource tcs = PrepareForShutdown(_conn); + return WaitAsync(tcs, "connection shutdown"); } - protected void WaitForShutdown(IConnection conn) + protected static Task WaitForShutdownAsync(IConnection conn) { - Wait(PrepareForShutdown(conn), "connection shutdown"); + TaskCompletionSource tcs = PrepareForShutdown(conn); + return WaitAsync(tcs, "connection shutdown"); } - protected void WithTemporaryQueueNoWait(IChannel channel, Action action, string queue) + protected async Task WithTemporaryExclusiveQueueNoWaitAsync(IChannel channel, Func action, string queue) { try { - channel.QueueDeclareNoWait(queue, false, true, false, null); - action(channel, queue); + await channel.QueueDeclareAsync(queue: queue, durable: false, exclusive: true, autoDelete: false, noWait: true); + await action(channel, queue); } finally { - WithTemporaryChannel(x => x.QueueDelete(queue)); + await WithTemporaryChannelAsync((ch) => ch.QueueDeleteAsync(queue)); } } public class AckingBasicConsumer : TestBasicConsumer { - public AckingBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch) + public AckingBasicConsumer(IChannel channel, ushort totalMessageCount, TaskCompletionSource allMessagesSeenLatch) : base(channel, totalMessageCount, allMessagesSeenLatch) { } - public override void PostHandleDelivery(ulong deliveryTag) + public override Task PostHandleDeliveryAsync(ulong deliveryTag) { - Channel.BasicAck(deliveryTag, false); + return Channel.BasicAckAsync(deliveryTag, false).AsTask(); } } public class NackingBasicConsumer : TestBasicConsumer { - public NackingBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch) - : base(channel, totalMessageCount, allMessagesSeenLatch) + public NackingBasicConsumer(IChannel channel, ushort totalMessageCount, TaskCompletionSource allMessagesSeenTcs) + : base(channel, totalMessageCount, allMessagesSeenTcs) { } - public override void PostHandleDelivery(ulong deliveryTag) + public override Task PostHandleDeliveryAsync(ulong deliveryTag) { - Channel.BasicNack(deliveryTag, false, false); + return Channel.BasicNackAsync(deliveryTag, false, false).AsTask(); } } public class RejectingBasicConsumer : TestBasicConsumer { - public RejectingBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch) - : base(channel, totalMessageCount, allMessagesSeenLatch) + public RejectingBasicConsumer(IChannel channel, ushort totalMessageCount, TaskCompletionSource allMessagesSeenTcs) + : base(channel, totalMessageCount, allMessagesSeenTcs) { } - public override void PostHandleDelivery(ulong deliveryTag) + public override Task PostHandleDeliveryAsync(ulong deliveryTag) { - Channel.BasicReject(deliveryTag, false); + return Channel.BasicRejectAsync(deliveryTag, false); } } public class TestBasicConsumer : DefaultBasicConsumer { - protected readonly ManualResetEventSlim _allMessagesSeenLatch; + protected readonly TaskCompletionSource _allMessagesSeenTcs; protected readonly ushort _totalMessageCount; protected ushort _counter = 0; - public TestBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch) + public TestBasicConsumer(IChannel channel, ushort totalMessageCount, TaskCompletionSource allMessagesSeenTcs) : base(channel) { _totalMessageCount = totalMessageCount; - _allMessagesSeenLatch = allMessagesSeenLatch; + _allMessagesSeenTcs = allMessagesSeenTcs; } - public override void HandleBasicDeliver(string consumerTag, + public override Task HandleBasicDeliverAsync(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, - in ReadOnlyBasicProperties properties, + ReadOnlyBasicProperties properties, ReadOnlyMemory body) { try { - PostHandleDelivery(deliveryTag); + return PostHandleDeliveryAsync(deliveryTag); } finally { ++_counter; if (_counter >= _totalMessageCount) { - _allMessagesSeenLatch.Set(); + _allMessagesSeenTcs.SetResult(true); } } } - public virtual void PostHandleDelivery(ulong deliveryTag) + public virtual Task PostHandleDeliveryAsync(ulong deliveryTag) { + return Task.CompletedTask; } } - protected bool SendAndConsumeMessage(IConnection conn, string queue, string exchange, string routingKey) + protected static async Task SendAndConsumeMessageAsync(IConnection conn, string queue, string exchange, string routingKey) { - using (IChannel ch = conn.CreateChannel()) + using (IChannel ch = await conn.CreateChannelAsync()) { - var latch = new ManualResetEventSlim(false); + await ch.ConfirmSelectAsync(); + + var tcs = new TaskCompletionSource(); + + var consumer = new AckingBasicConsumer(ch, 1, tcs); - var consumer = new AckingBasicConsumer(ch, 1, latch); + await ch.BasicConsumeAsync(queue, false, consumer); - ch.BasicConsume(queue, false, consumer); + await ch.BasicPublishAsync(exchange: exchange, routingKey: routingKey, + body: _encoding.GetBytes("test message"), mandatory: true); - ch.BasicPublish(exchange, routingKey, _encoding.GetBytes("test message")); + await ch.WaitForConfirmsOrDieAsync(); - return latch.Wait(TimeSpan.FromSeconds(5)); + try + { + await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + return tcs.Task.IsCompletedSuccessfully(); + } + catch (TimeoutException) + { + return false; + } + finally + { + await ch.CloseAsync(); + } } } } diff --git a/projects/Test/SequentialIntegration/TestConnectionRecoveryWithoutSetup.cs b/projects/Test/SequentialIntegration/TestConnectionRecoveryWithoutSetup.cs index b88c90451f..57690db17b 100644 --- a/projects/Test/SequentialIntegration/TestConnectionRecoveryWithoutSetup.cs +++ b/projects/Test/SequentialIntegration/TestConnectionRecoveryWithoutSetup.cs @@ -31,7 +31,7 @@ using System; using System.Collections.Generic; -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; using RabbitMQ.Client.Exceptions; @@ -47,39 +47,44 @@ public TestConnectionRecoveryWithoutSetup(ITestOutputHelper output) : base(outpu { } - protected override void SetUp() + public override Task InitializeAsync() { + // NB: nothing to do here since each test creates its own factory, + // connections and channels Assert.Null(_connFactory); Assert.Null(_conn); Assert.Null(_channel); + return Task.CompletedTask; } [Fact] - public void TestBasicConnectionRecoveryWithHostnameList() + public async Task TestBasicConnectionRecoveryWithHostnameList() { - using (AutorecoveringConnection c = CreateAutorecoveringConnection(new List { "127.0.0.1", "localhost" })) + using (AutorecoveringConnection c = await CreateAutorecoveringConnectionAsync(new List { "127.0.0.1", "localhost" })) { Assert.True(c.IsOpen); - CloseAndWaitForRecovery(c); + await CloseAndWaitForRecoveryAsync(c); Assert.True(c.IsOpen); + await c.CloseAsync(); } } [Fact] - public void TestBasicConnectionRecoveryWithHostnameListAndUnreachableHosts() + public async Task TestBasicConnectionRecoveryWithHostnameListAndUnreachableHosts() { - using (AutorecoveringConnection c = CreateAutorecoveringConnection(new List { "191.72.44.22", "127.0.0.1", "localhost" })) + using (AutorecoveringConnection c = await CreateAutorecoveringConnectionAsync(new List { "191.72.44.22", "127.0.0.1", "localhost" })) { Assert.True(c.IsOpen); - CloseAndWaitForRecovery(c); + await CloseAndWaitForRecoveryAsync(c); Assert.True(c.IsOpen); + await c.CloseAsync(); } } [Fact] - public void TestBasicConnectionRecoveryWithEndpointList() + public async Task TestBasicConnectionRecoveryWithEndpointList() { - using (AutorecoveringConnection c = CreateAutorecoveringConnection( + using (AutorecoveringConnection c = await CreateAutorecoveringConnectionAsync( new List { new AmqpTcpEndpoint("127.0.0.1"), @@ -87,15 +92,16 @@ public void TestBasicConnectionRecoveryWithEndpointList() })) { Assert.True(c.IsOpen); - CloseAndWaitForRecovery(c); + await CloseAndWaitForRecoveryAsync(c); Assert.True(c.IsOpen); + await c.CloseAsync(); } } [Fact] - public void TestBasicConnectionRecoveryWithEndpointListAndUnreachableHosts() + public async Task TestBasicConnectionRecoveryWithEndpointListAndUnreachableHosts() { - using (AutorecoveringConnection c = CreateAutorecoveringConnection( + using (AutorecoveringConnection c = await CreateAutorecoveringConnectionAsync( new List { new AmqpTcpEndpoint("191.72.44.22"), @@ -104,229 +110,273 @@ public void TestBasicConnectionRecoveryWithEndpointListAndUnreachableHosts() })) { Assert.True(c.IsOpen); - CloseAndWaitForRecovery(c); + await CloseAndWaitForRecoveryAsync(c); Assert.True(c.IsOpen); + await c.CloseAsync(); } } [Fact] - public void TestConsumerWorkServiceRecovery() + public async Task TestConsumerWorkServiceRecovery() { - using (AutorecoveringConnection c = CreateAutorecoveringConnection()) + using (AutorecoveringConnection c = await CreateAutorecoveringConnectionAsync()) { - IChannel m = c.CreateChannel(); - string q = m.QueueDeclare("dotnet-client.recovery.consumer_work_pool1", - false, false, false, null).QueueName; - var cons = new EventingBasicConsumer(m); - m.BasicConsume(q, true, cons); - AssertConsumerCount(m, q, 1); + using (IChannel ch = await c.CreateChannelAsync()) + { + string q = (await ch.QueueDeclareAsync("dotnet-client.recovery.consumer_work_pool1", + false, false, false)).QueueName; + var cons = new EventingBasicConsumer(ch); + await ch.BasicConsumeAsync(q, true, cons); + await AssertConsumerCountAsync(ch, q, 1); + + await CloseAndWaitForRecoveryAsync(c); - CloseAndWaitForRecovery(c); + Assert.True(ch.IsOpen); + var tcs = new TaskCompletionSource(); + cons.Received += (s, args) => tcs.SetResult(true); - Assert.True(m.IsOpen); - var latch = new ManualResetEventSlim(false); - cons.Received += (s, args) => latch.Set(); + await ch.BasicPublishAsync("", q, _encoding.GetBytes("msg")); + await WaitAsync(tcs, "received event"); - m.BasicPublish("", q, _encoding.GetBytes("msg")); - Wait(latch, "received event"); + await ch.QueueDeleteAsync(q); + await ch.CloseAsync(); + } - m.QueueDelete(q); + await c.CloseAsync(); } } [Fact] - public void TestConsumerRecoveryOnClientNamedQueueWithOneRecovery() + public async Task TestConsumerRecoveryOnClientNamedQueueWithOneRecovery() { string q0 = "dotnet-client.recovery.queue1"; - using (AutorecoveringConnection c = CreateAutorecoveringConnection()) + using (AutorecoveringConnection c = await CreateAutorecoveringConnectionAsync()) { - IChannel m = c.CreateChannel(); - string q1 = m.QueueDeclare(q0, false, false, false, null).QueueName; - Assert.Equal(q0, q1); + using (IChannel ch = await c.CreateChannelAsync()) + { + string q1 = (await ch.QueueDeclareAsync(q0, false, false, false)).QueueName; + Assert.Equal(q0, q1); + + var cons = new EventingBasicConsumer(ch); + await ch.BasicConsumeAsync(q1, true, cons); + await AssertConsumerCountAsync(ch, q1, 1); - var cons = new EventingBasicConsumer(m); - m.BasicConsume(q1, true, cons); - AssertConsumerCount(m, q1, 1); + bool queueNameChangeAfterRecoveryCalled = false; - bool queueNameChangeAfterRecoveryCalled = false; + c.QueueNameChangedAfterRecovery += (source, ea) => { queueNameChangeAfterRecoveryCalled = true; }; - c.QueueNameChangedAfterRecovery += (source, ea) => { queueNameChangeAfterRecoveryCalled = true; }; + await CloseAndWaitForRecoveryAsync(c); + await AssertConsumerCountAsync(ch, q1, 1); + Assert.False(queueNameChangeAfterRecoveryCalled); - CloseAndWaitForRecovery(c); - AssertConsumerCount(m, q1, 1); - Assert.False(queueNameChangeAfterRecoveryCalled); + await CloseAndWaitForRecoveryAsync(c); + await AssertConsumerCountAsync(ch, q1, 1); + Assert.False(queueNameChangeAfterRecoveryCalled); - CloseAndWaitForRecovery(c); - AssertConsumerCount(m, q1, 1); - Assert.False(queueNameChangeAfterRecoveryCalled); + await CloseAndWaitForRecoveryAsync(c); + await AssertConsumerCountAsync(ch, q1, 1); + Assert.False(queueNameChangeAfterRecoveryCalled); - CloseAndWaitForRecovery(c); - AssertConsumerCount(m, q1, 1); - Assert.False(queueNameChangeAfterRecoveryCalled); + var tcs = new TaskCompletionSource(); + cons.Received += (s, args) => tcs.SetResult(true); - var latch = new ManualResetEventSlim(false); - cons.Received += (s, args) => latch.Set(); + await ch.BasicPublishAsync("", q1, _encoding.GetBytes("msg")); + await WaitAsync(tcs, "received event"); - m.BasicPublish("", q1, _encoding.GetBytes("msg")); - Wait(latch, "received event"); + await ch.QueueDeleteAsync(q1); + await ch.CloseAsync(); + } - m.QueueDelete(q1); + await c.CloseAsync(); } } [Fact] - public void TestConsumerRecoveryWithServerNamedQueue() + public async Task TestConsumerRecoveryWithServerNamedQueue() { // https://github.com/rabbitmq/rabbitmq-dotnet-client/issues/1238 - using (AutorecoveringConnection c = CreateAutorecoveringConnection()) + using (AutorecoveringConnection c = await CreateAutorecoveringConnectionAsync()) { - IChannel ch = c.CreateChannel(); - RabbitMQ.Client.QueueDeclareOk queueDeclareResult = ch.QueueDeclare(queue: string.Empty, durable: false, exclusive: true, autoDelete: true, arguments: null); - string qname = queueDeclareResult.QueueName; - Assert.False(string.IsNullOrEmpty(qname)); - - var cons = new EventingBasicConsumer(ch); - ch.BasicConsume(string.Empty, true, cons); - AssertConsumerCount(ch, qname, 1); - - bool queueNameBeforeIsEqual = false; - bool queueNameChangeAfterRecoveryCalled = false; - string qnameAfterRecovery = null; - c.QueueNameChangedAfterRecovery += (source, ea) => + using (IChannel ch = await c.CreateChannelAsync()) { - queueNameChangeAfterRecoveryCalled = true; - queueNameBeforeIsEqual = qname.Equals(ea.NameBefore); - qnameAfterRecovery = ea.NameAfter; - }; - - CloseAndWaitForRecovery(c); + RabbitMQ.Client.QueueDeclareOk queueDeclareResult = + await ch.QueueDeclareAsync(queue: string.Empty, durable: false, exclusive: true, autoDelete: true, arguments: null); + string qname = queueDeclareResult.QueueName; + Assert.False(string.IsNullOrEmpty(qname)); + + var cons = new EventingBasicConsumer(ch); + await ch.BasicConsumeAsync(string.Empty, true, cons); + await AssertConsumerCountAsync(ch, qname, 1); + + bool queueNameBeforeIsEqual = false; + bool queueNameChangeAfterRecoveryCalled = false; + string qnameAfterRecovery = null; + c.QueueNameChangedAfterRecovery += (source, ea) => + { + queueNameChangeAfterRecoveryCalled = true; + queueNameBeforeIsEqual = qname.Equals(ea.NameBefore); + qnameAfterRecovery = ea.NameAfter; + }; + + await CloseAndWaitForRecoveryAsync(c); + + await AssertConsumerCountAsync(ch, qnameAfterRecovery, 1); + Assert.True(queueNameChangeAfterRecoveryCalled); + Assert.True(queueNameBeforeIsEqual); + + await ch.CloseAsync(); + } - AssertConsumerCount(ch, qnameAfterRecovery, 1); - Assert.True(queueNameChangeAfterRecoveryCalled); - Assert.True(queueNameBeforeIsEqual); + await c.CloseAsync(); } } [Fact] - public void TestCreateChannelOnClosedAutorecoveringConnectionDoesNotHang() + public async Task TestCreateChannelOnClosedAutorecoveringConnectionDoesNotHang() { // we don't want this to recover quickly in this test - AutorecoveringConnection c = CreateAutorecoveringConnection(TimeSpan.FromSeconds(20)); - - try - { - c.Close(); - WaitForShutdown(c); - Assert.False(c.IsOpen); - c.CreateChannel(); - Assert.Fail("Expected an exception"); - } - catch (AlreadyClosedException) + using (AutorecoveringConnection conn = await CreateAutorecoveringConnectionAsync(TimeSpan.FromSeconds(20))) { - // expected - } - finally - { - StartRabbitMQ(); - if (c.IsOpen) + try + { + await conn.CloseAsync(); + await WaitForShutdownAsync(conn); + Assert.False(conn.IsOpen); + await conn.CreateChannelAsync(); + Assert.Fail("Expected an exception"); + } + catch (AlreadyClosedException) + { + // expected + } + finally { - c.Abort(); + await StartRabbitMqAsync(); + await conn.CloseAsync(); } } } [Fact] - public void TestTopologyRecoveryConsumerFilter() + public async Task TestTopologyRecoveryConsumerFilter() { var filter = new TopologyRecoveryFilter { ConsumerFilter = consumer => !consumer.ConsumerTag.Contains("filtered") }; - var latch = new ManualResetEventSlim(false); - AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter); - conn.RecoverySucceeded += (source, ea) => latch.Set(); - IChannel ch = conn.CreateChannel(); - ch.ConfirmSelect(); - - var exchange = "topology.recovery.exchange"; - var queueWithRecoveredConsumer = "topology.recovery.queue.1"; - var queueWithIgnoredConsumer = "topology.recovery.queue.2"; - var binding1 = "recovered.binding.1"; - var binding2 = "recovered.binding.2"; - - ch.ExchangeDeclare(exchange, "direct"); - ch.QueueDeclare(queueWithRecoveredConsumer, false, false, false, null); - ch.QueueDeclare(queueWithIgnoredConsumer, false, false, false, null); - ch.QueueBind(queueWithRecoveredConsumer, exchange, binding1); - ch.QueueBind(queueWithIgnoredConsumer, exchange, binding2); - ch.QueuePurge(queueWithRecoveredConsumer); - ch.QueuePurge(queueWithIgnoredConsumer); - - var recoverLatch = new ManualResetEventSlim(false); - var consumerToRecover = new EventingBasicConsumer(ch); - consumerToRecover.Received += (source, ea) => recoverLatch.Set(); - ch.BasicConsume(queueWithRecoveredConsumer, true, "recovered.consumer", consumerToRecover); - - var ignoredLatch = new ManualResetEventSlim(false); - var consumerToIgnore = new EventingBasicConsumer(ch); - consumerToIgnore.Received += (source, ea) => ignoredLatch.Set(); - ch.BasicConsume(queueWithIgnoredConsumer, true, "filtered.consumer", consumerToIgnore); - - try - { - CloseAndWaitForRecovery(conn); - Wait(latch, "recovery succeeded"); - - Assert.True(ch.IsOpen); - ch.BasicPublish(exchange, binding1, _encoding.GetBytes("test message")); - ch.BasicPublish(exchange, binding2, _encoding.GetBytes("test message")); - Assert.True(recoverLatch.Wait(TimeSpan.FromSeconds(5))); - Assert.False(ignoredLatch.Wait(TimeSpan.FromSeconds(5))); + var connectionRecoveryTcs = new TaskCompletionSource(); - ch.BasicConsume(queueWithIgnoredConsumer, true, "filtered.consumer", consumerToIgnore); + using (AutorecoveringConnection conn = await CreateAutorecoveringConnectionWithTopologyRecoveryFilterAsync(filter)) + { + conn.RecoverySucceeded += (source, ea) => connectionRecoveryTcs.SetResult(true); - try - { - ch.BasicConsume(queueWithRecoveredConsumer, true, "recovered.consumer", consumerToRecover); - Assert.Fail("Expected an exception"); - } - catch (OperationInterruptedException e) + using (IChannel ch = await conn.CreateChannelAsync()) { - AssertShutdownError(e.ShutdownReason, 530); // NOT_ALLOWED - not allowed to reuse consumer tag + await ch.ConfirmSelectAsync(); + + string exchange = "topology.recovery.exchange"; + string queueWithRecoveredConsumer = "topology.recovery.queue.1"; + string queueWithIgnoredConsumer = "topology.recovery.queue.2"; + string binding1 = "recovered.binding.1"; + string binding2 = "recovered.binding.2"; + + await ch.ExchangeDeclareAsync(exchange, "direct"); + await ch.QueueDeclareAsync(queueWithRecoveredConsumer, false, false, false); + await ch.QueueDeclareAsync(queueWithIgnoredConsumer, false, false, false); + await ch.QueueBindAsync(queueWithRecoveredConsumer, exchange, binding1); + await ch.QueueBindAsync(queueWithIgnoredConsumer, exchange, binding2); + await ch.QueuePurgeAsync(queueWithRecoveredConsumer); + await ch.QueuePurgeAsync(queueWithIgnoredConsumer); + + var consumerRecoveryTcs = new TaskCompletionSource(); + var consumerToRecover = new EventingBasicConsumer(ch); + consumerToRecover.Received += (source, ea) => consumerRecoveryTcs.SetResult(true); + await ch.BasicConsumeAsync(queueWithRecoveredConsumer, true, "recovered.consumer", consumerToRecover); + + var ignoredTcs = new TaskCompletionSource(); + var consumerToIgnore = new EventingBasicConsumer(ch); + consumerToIgnore.Received += (source, ea) => ignoredTcs.SetResult(true); + await ch.BasicConsumeAsync(queueWithIgnoredConsumer, true, "filtered.consumer", consumerToIgnore); + + try + { + await CloseAndWaitForRecoveryAsync(conn); + await WaitAsync(connectionRecoveryTcs, "recovery succeeded"); + + Assert.True(ch.IsOpen); + await ch.BasicPublishAsync(exchange, binding1, _encoding.GetBytes("test message")); + await ch.BasicPublishAsync(exchange, binding2, _encoding.GetBytes("test message")); + + await consumerRecoveryTcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + Assert.True(await consumerRecoveryTcs.Task); + + bool sawTimeout = false; + try + { + await ignoredTcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + } + catch (TimeoutException) + { + sawTimeout = true; + } + Assert.True(sawTimeout); + + await ch.BasicConsumeAsync(queueWithIgnoredConsumer, true, "filtered.consumer", consumerToIgnore); + + try + { + await ch.BasicConsumeAsync(queueWithRecoveredConsumer, true, "recovered.consumer", consumerToRecover); + Assert.Fail("Expected an exception"); + } + catch (OperationInterruptedException e) + { + AssertShutdownError(e.ShutdownReason, 530); // NOT_ALLOWED - not allowed to reuse consumer tag + } + } + finally + { + await ch.CloseAsync(); + } } - } - finally - { - conn.Abort(); + + await conn.CloseAsync(); } } [Fact] - public void TestRecoveryWithTopologyDisabled() + public async Task TestRecoveryWithTopologyDisabled() { - AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryDisabled(); - IChannel ch = conn.CreateChannel(); - string s = "dotnet-client.test.recovery.q2"; - ch.QueueDelete(s); - ch.QueueDeclare(s, false, true, false, null); - ch.QueueDeclarePassive(s); - Assert.True(ch.IsOpen); - - try - { - CloseAndWaitForRecovery(conn); - Assert.True(ch.IsOpen); - ch.QueueDeclarePassive(s); - Assert.Fail("Expected an exception"); - } - catch (OperationInterruptedException) + using (AutorecoveringConnection conn = await CreateAutorecoveringConnectionWithTopologyRecoveryDisabledAsync()) { - // expected - } - finally - { - conn.Abort(); + using (IChannel ch = await conn.CreateChannelAsync()) + { + try + { + string s = "dotnet-client.test.recovery.q2"; + await ch.QueueDeleteAsync(s); + await ch.QueueDeclareAsync(queue: s, durable: false, exclusive: true, autoDelete: false, arguments: null); + await ch.QueueDeclareAsync(queue: s, passive: true, durable: false, exclusive: true, autoDelete: false, arguments: null); + + Assert.True(ch.IsOpen); + await CloseAndWaitForRecoveryAsync(conn); + + Assert.True(ch.IsOpen); + await ch.QueueDeclareAsync(queue: s, passive: true, durable: false, exclusive: true, autoDelete: false, arguments: null); + + Assert.Fail("Expected an exception"); + } + catch (OperationInterruptedException) + { + // expected + } + finally + { + await ch.CloseAsync(); + } + } + + await conn.CloseAsync(); } } } diff --git a/projects/Test/SequentialIntegration/TestConnectionTopologyRecovery.cs b/projects/Test/SequentialIntegration/TestConnectionTopologyRecovery.cs index 2435dbb90a..f58b1d9ecd 100644 --- a/projects/Test/SequentialIntegration/TestConnectionTopologyRecovery.cs +++ b/projects/Test/SequentialIntegration/TestConnectionTopologyRecovery.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; using RabbitMQ.Client.Exceptions; @@ -48,211 +49,248 @@ public TestConnectionTopologyRecovery(ITestOutputHelper output) : base(output) } [Fact] - public void TestRecoverTopologyOnDisposedChannel() + public async Task TestRecoverTopologyOnDisposedChannel() { string x = GenerateExchangeName(); string q = GenerateQueueName(); const string rk = "routing-key"; - using (IChannel ch = _conn.CreateChannel()) + using (IChannel ch = await _conn.CreateChannelAsync()) { - ch.ExchangeDeclare(exchange: x, type: "fanout"); - ch.QueueDeclare(q, false, false, false, null); - ch.QueueBind(q, x, rk); + await ch.ExchangeDeclareAsync(exchange: x, type: "fanout"); + await ch.QueueDeclareAsync(q, false, false, false); + await ch.QueueBindAsync(q, x, rk); + await ch.CloseAsync(); } var cons = new EventingBasicConsumer(_channel); - _channel.BasicConsume(q, true, cons); - AssertConsumerCount(_channel, q, 1); + await _channel.BasicConsumeAsync(q, true, cons); + await AssertConsumerCountAsync(_channel, q, 1); - CloseAndWaitForRecovery(); - AssertConsumerCount(_channel, q, 1); + await CloseAndWaitForRecoveryAsync(); + await AssertConsumerCountAsync(_channel, q, 1); - var latch = new ManualResetEventSlim(false); - cons.Received += (s, args) => latch.Set(); + var tcs = new TaskCompletionSource(); + cons.Received += (s, args) => tcs.SetResult(true); - _channel.BasicPublish("", q, _messageBody); - Wait(latch, "received event"); + await _channel.BasicPublishAsync("", q, _messageBody); + await WaitAsync(tcs, "received event"); - _channel.QueueUnbind(q, x, rk); - _channel.ExchangeDelete(x); - _channel.QueueDelete(q); + await _channel.QueueUnbindAsync(q, x, rk); + await _channel.ExchangeDeleteAsync(x); + await _channel.QueueDeleteAsync(q); } [Fact] - public void TestTopologyRecoveryQueueFilter() + public async Task TestTopologyRecoveryQueueFilter() { - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); var filter = new TopologyRecoveryFilter { QueueFilter = queue => !queue.Name.Contains("filtered") }; - using AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter); - conn.RecoverySucceeded += (source, ea) => latch.Set(); - using IChannel ch = conn.CreateChannel(); + AutorecoveringConnection conn = await CreateAutorecoveringConnectionWithTopologyRecoveryFilterAsync(filter); + conn.RecoverySucceeded += (source, ea) => tcs.SetResult(true); + IChannel ch = await conn.CreateChannelAsync(); string queueToRecover = "recovered.queue"; string queueToIgnore = "filtered.queue"; - ch.QueueDeclare(queueToRecover, false, false, false, null); - ch.QueueDeclare(queueToIgnore, false, false, false, null); + await ch.QueueDeclareAsync(queueToRecover, false, false, false); + await ch.QueueDeclareAsync(queueToIgnore, false, false, false); - _channel.QueueDelete(queueToRecover); - _channel.QueueDelete(queueToIgnore); + await _channel.QueueDeleteAsync(queueToRecover); + await _channel.QueueDeleteAsync(queueToIgnore); - CloseAndWaitForRecovery(conn); - Wait(latch, "recovery succeeded"); + await CloseAndWaitForRecoveryAsync(conn); + await WaitAsync(tcs, "recovery succeeded"); Assert.True(ch.IsOpen); - AssertQueueRecovery(ch, queueToRecover, false); + await AssertQueueRecoveryAsync(ch, queueToRecover, false); try { - AssertQueueRecovery(ch, queueToIgnore, false); + await AssertQueueRecoveryAsync(ch, queueToIgnore, false); Assert.Fail("Expected an exception"); } catch (OperationInterruptedException e) { - AssertShutdownError(e.ShutdownReason, 404); + IntegrationFixtureBase.AssertShutdownError(e.ShutdownReason, 404); + } + finally + { + await ch.CloseAsync(); + await conn.CloseAsync(); + ch.Dispose(); + conn.Dispose(); } } [Fact] - public void TestTopologyRecoveryExchangeFilter() + public async Task TestTopologyRecoveryExchangeFilter() { - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); var filter = new TopologyRecoveryFilter { ExchangeFilter = exchange => exchange.Type == "topic" && !exchange.Name.Contains("filtered") }; - using AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter); - conn.RecoverySucceeded += (source, ea) => latch.Set(); - using IChannel ch = conn.CreateChannel(); - - string exchangeToRecover = "recovered.exchange"; - string exchangeToIgnore = "filtered.exchange"; - ch.ExchangeDeclare(exchangeToRecover, "topic", false, true); - ch.ExchangeDeclare(exchangeToIgnore, "direct", false, true); - - _channel.ExchangeDelete(exchangeToRecover); - _channel.ExchangeDelete(exchangeToIgnore); + AutorecoveringConnection conn = await CreateAutorecoveringConnectionWithTopologyRecoveryFilterAsync(filter); + conn.RecoverySucceeded += (source, ea) => tcs.SetResult(true); + IChannel ch = await conn.CreateChannelAsync(); + try + { + string exchangeToRecover = "recovered.exchange"; + string exchangeToIgnore = "filtered.exchange"; + await ch.ExchangeDeclareAsync(exchangeToRecover, "topic", false, true); + await ch.ExchangeDeclareAsync(exchangeToIgnore, "direct", false, true); - CloseAndWaitForRecovery(conn); - Wait(latch, "recovery succeeded"); + await _channel.ExchangeDeleteAsync(exchangeToRecover); + await _channel.ExchangeDeleteAsync(exchangeToIgnore); - Assert.True(ch.IsOpen); - AssertExchangeRecovery(ch, exchangeToRecover); + await CloseAndWaitForRecoveryAsync(conn); + await WaitAsync(tcs, "recovery succeeded"); - try - { - AssertExchangeRecovery(ch, exchangeToIgnore); + Assert.True(ch.IsOpen); + await AssertExchangeRecoveryAsync(ch, exchangeToRecover); + await AssertExchangeRecoveryAsync(ch, exchangeToIgnore); Assert.Fail("Expected an exception"); } catch (OperationInterruptedException e) { AssertShutdownError(e.ShutdownReason, 404); } + finally + { + await ch.CloseAsync(); + await conn.CloseAsync(); + ch.Dispose(); + conn.Dispose(); + } } [Fact] - public void TestTopologyRecoveryBindingFilter() + public async Task TestTopologyRecoveryBindingFilter() { - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); var filter = new TopologyRecoveryFilter { BindingFilter = binding => !binding.RoutingKey.Contains("filtered") }; - using AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter); - conn.RecoverySucceeded += (source, ea) => latch.Set(); - using IChannel ch = conn.CreateChannel(); - - string exchange = "topology.recovery.exchange"; - string queueWithRecoveredBinding = "topology.recovery.queue.1"; - string queueWithIgnoredBinding = "topology.recovery.queue.2"; - string bindingToRecover = "recovered.binding"; - string bindingToIgnore = "filtered.binding"; - - ch.ExchangeDeclare(exchange, "direct"); - ch.QueueDeclare(queueWithRecoveredBinding, false, false, false, null); - ch.QueueDeclare(queueWithIgnoredBinding, false, false, false, null); - ch.QueueBind(queueWithRecoveredBinding, exchange, bindingToRecover); - ch.QueueBind(queueWithIgnoredBinding, exchange, bindingToIgnore); - ch.QueuePurge(queueWithRecoveredBinding); - ch.QueuePurge(queueWithIgnoredBinding); - - _channel.QueueUnbind(queueWithRecoveredBinding, exchange, bindingToRecover); - _channel.QueueUnbind(queueWithIgnoredBinding, exchange, bindingToIgnore); - - CloseAndWaitForRecovery(conn); - Wait(latch, "recovery succeeded"); + AutorecoveringConnection conn = await CreateAutorecoveringConnectionWithTopologyRecoveryFilterAsync(filter); + conn.RecoverySucceeded += (source, ea) => tcs.SetResult(true); + IChannel ch = await conn.CreateChannelAsync(); + try + { + string exchange = "topology.recovery.exchange"; + string queueWithRecoveredBinding = "topology.recovery.queue.1"; + string queueWithIgnoredBinding = "topology.recovery.queue.2"; + string bindingToRecover = "recovered.binding"; + string bindingToIgnore = "filtered.binding"; + + await ch.ExchangeDeclareAsync(exchange, "direct"); + await ch.QueueDeclareAsync(queueWithRecoveredBinding, false, false, false); + await ch.QueueDeclareAsync(queueWithIgnoredBinding, false, false, false); + await ch.QueueBindAsync(queueWithRecoveredBinding, exchange, bindingToRecover); + await ch.QueueBindAsync(queueWithIgnoredBinding, exchange, bindingToIgnore); + await ch.QueuePurgeAsync(queueWithRecoveredBinding); + await ch.QueuePurgeAsync(queueWithIgnoredBinding); + + await _channel.QueueUnbindAsync(queueWithRecoveredBinding, exchange, bindingToRecover); + await _channel.QueueUnbindAsync(queueWithIgnoredBinding, exchange, bindingToIgnore); + + await CloseAndWaitForRecoveryAsync(conn); + await WaitAsync(tcs, "recovery succeeded"); - Assert.True(ch.IsOpen); - Assert.True(SendAndConsumeMessage(_conn, queueWithRecoveredBinding, exchange, bindingToRecover)); - Assert.False(SendAndConsumeMessage(_conn, queueWithIgnoredBinding, exchange, bindingToIgnore)); + Assert.True(ch.IsOpen); + Assert.True(await SendAndConsumeMessageAsync(_conn, queueWithRecoveredBinding, exchange, bindingToRecover)); + Assert.False(await SendAndConsumeMessageAsync(_conn, queueWithIgnoredBinding, exchange, bindingToIgnore)); + } + finally + { + await ch.CloseAsync(); + await conn.CloseAsync(); + ch.Dispose(); + conn.Dispose(); + } } [Fact] - public void TestTopologyRecoveryDefaultFilterRecoversAllEntities() + public async Task TestTopologyRecoveryDefaultFilterRecoversAllEntities() { - var latch = new ManualResetEventSlim(false); + var connectionRecoveryTcs = new TaskCompletionSource(); var filter = new TopologyRecoveryFilter(); - using AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter); - conn.RecoverySucceeded += (source, ea) => latch.Set(); - using IChannel ch = conn.CreateChannel(); - ch.ConfirmSelect(); - - string exchange = "topology.recovery.exchange"; - string queue1 = "topology.recovery.queue.1"; - string queue2 = "topology.recovery.queue.2"; - string binding1 = "recovered.binding"; - string binding2 = "filtered.binding"; - - ch.ExchangeDeclare(exchange, "direct"); - ch.QueueDeclare(queue1, false, false, false, null); - ch.QueueDeclare(queue2, false, false, false, null); - ch.QueueBind(queue1, exchange, binding1); - ch.QueueBind(queue2, exchange, binding2); - ch.QueuePurge(queue1); - ch.QueuePurge(queue2); - - var consumerLatch1 = new ManualResetEventSlim(false); - var consumer1 = new EventingBasicConsumer(ch); - consumer1.Received += (source, ea) => consumerLatch1.Set(); - ch.BasicConsume(queue1, true, "recovered.consumer", consumer1); - - var consumerLatch2 = new ManualResetEventSlim(false); - var consumer2 = new EventingBasicConsumer(ch); - consumer2.Received += (source, ea) => consumerLatch2.Set(); - ch.BasicConsume(queue2, true, "filtered.consumer", consumer2); - - _channel.ExchangeDelete(exchange); - _channel.QueueDelete(queue1); - _channel.QueueDelete(queue2); - - CloseAndWaitForRecovery(conn); - Wait(latch, "recovery succeeded"); + AutorecoveringConnection conn = await CreateAutorecoveringConnectionWithTopologyRecoveryFilterAsync(filter); + conn.RecoverySucceeded += (source, ea) => connectionRecoveryTcs.SetResult(true); + IChannel ch = await conn.CreateChannelAsync(); + try + { + await ch.ConfirmSelectAsync(); + + string exchange = "topology.recovery.exchange"; + string queue1 = "topology.recovery.queue.1"; + string queue2 = "topology.recovery.queue.2"; + string binding1 = "recovered.binding"; + string binding2 = "filtered.binding"; + + await ch.ExchangeDeclareAsync(exchange, "direct"); + await ch.QueueDeclareAsync(queue1, false, false, false); + await ch.QueueDeclareAsync(queue2, false, false, false); + await ch.QueueBindAsync(queue1, exchange, binding1); + await ch.QueueBindAsync(queue2, exchange, binding2); + await ch.QueuePurgeAsync(queue1); + await ch.QueuePurgeAsync(queue2); + + var consumerReceivedTcs1 = new TaskCompletionSource(); + var consumer1 = new EventingBasicConsumer(ch); + consumer1.Received += (source, ea) => consumerReceivedTcs1.SetResult(true); + await ch.BasicConsumeAsync(queue1, true, "recovered.consumer", consumer1); + + var consumerReceivedTcs2 = new TaskCompletionSource(); + var consumer2 = new EventingBasicConsumer(ch); + consumer2.Received += (source, ea) => consumerReceivedTcs2.SetResult(true); + await ch.BasicConsumeAsync(queue2, true, "filtered.consumer", consumer2); + + await _channel.ExchangeDeleteAsync(exchange); + await _channel.QueueDeleteAsync(queue1); + await _channel.QueueDeleteAsync(queue2); + + await CloseAndWaitForRecoveryAsync(conn); + await WaitAsync(connectionRecoveryTcs, "recovery succeeded"); - Assert.True(ch.IsOpen); - AssertExchangeRecovery(ch, exchange); - ch.QueueDeclarePassive(queue1); - ch.QueueDeclarePassive(queue2); + Assert.True(ch.IsOpen); + await AssertExchangeRecoveryAsync(ch, exchange); + await ch.QueueDeclarePassiveAsync(queue1); + await ch.QueueDeclarePassiveAsync(queue2); + + await ch.BasicPublishAsync(exchange, binding1, _encoding.GetBytes("test message"), mandatory: true); + // await ch.WaitForConfirmsOrDieAsync(); - ch.BasicPublish(exchange, binding1, _encoding.GetBytes("test message")); - ch.BasicPublish(exchange, binding2, _encoding.GetBytes("test message")); + await ch.BasicPublishAsync(exchange, binding2, _encoding.GetBytes("test message"), mandatory: true); + // await ch.WaitForConfirmsOrDieAsync(); - Assert.True(consumerLatch1.Wait(TimeSpan.FromSeconds(5))); - Assert.True(consumerLatch2.Wait(TimeSpan.FromSeconds(5))); + await consumerReceivedTcs1.Task.WaitAsync(TimeSpan.FromSeconds(5)); + await consumerReceivedTcs2.Task.WaitAsync(TimeSpan.FromSeconds(5)); + Assert.True(consumerReceivedTcs1.Task.IsCompletedSuccessfully()); + Assert.True(consumerReceivedTcs2.Task.IsCompletedSuccessfully()); + } + finally + { + await ch.CloseAsync(); + await conn.CloseAsync(); + ch.Dispose(); + conn.Dispose(); + } } [Fact] - public void TestTopologyRecoveryQueueExceptionHandler() + public async Task TestTopologyRecoveryQueueExceptionHandler() { - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); var changedQueueArguments = new Dictionary { @@ -267,47 +305,54 @@ public void TestTopologyRecoveryQueueExceptionHandler() && ex is OperationInterruptedException operationInterruptedException && operationInterruptedException.ShutdownReason.ReplyCode == Constants.PreconditionFailed; }, - QueueRecoveryExceptionHandler = (rq, ex, connection) => + QueueRecoveryExceptionHandlerAsync = async (rq, ex, connection) => { - using (IChannel channel = connection.CreateChannel()) + using (IChannel channel = await connection.CreateChannelAsync()) { - channel.QueueDeclare(rq.Name, false, false, false, changedQueueArguments); + await channel.QueueDeclareAsync(rq.Name, false, false, false, + noWait: false, arguments: changedQueueArguments); + await channel.CloseAsync(); } } }; - using AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(exceptionHandler); - conn.RecoverySucceeded += (source, ea) => latch.Set(); - using IChannel ch = conn.CreateChannel(); + AutorecoveringConnection conn = await CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandlerAsync(exceptionHandler); + conn.RecoverySucceeded += (source, ea) => tcs.SetResult(true); + IChannel ch = await conn.CreateChannelAsync(); string queueToRecoverWithException = "recovery.exception.queue"; string queueToRecoverSuccessfully = "successfully.recovered.queue"; - ch.QueueDeclare(queueToRecoverWithException, false, false, false, null); - ch.QueueDeclare(queueToRecoverSuccessfully, false, false, false, null); + await ch.QueueDeclareAsync(queueToRecoverWithException, false, false, false); + await ch.QueueDeclareAsync(queueToRecoverSuccessfully, false, false, false); - _channel.QueueDelete(queueToRecoverSuccessfully); - _channel.QueueDelete(queueToRecoverWithException); - _channel.QueueDeclare(queueToRecoverWithException, false, false, false, changedQueueArguments); + await _channel.QueueDeleteAsync(queueToRecoverSuccessfully); + await _channel.QueueDeleteAsync(queueToRecoverWithException); + await _channel.QueueDeclareAsync(queueToRecoverWithException, false, false, false, + noWait: false, arguments: changedQueueArguments); try { - CloseAndWaitForRecovery(conn); - Wait(latch, "recovery succeded"); + await CloseAndWaitForRecoveryAsync(conn); + await WaitAsync(tcs, "recovery succeded"); Assert.True(ch.IsOpen); - AssertQueueRecovery(ch, queueToRecoverSuccessfully, false); - AssertQueueRecovery(ch, queueToRecoverWithException, false, changedQueueArguments); + await AssertQueueRecoveryAsync(ch, queueToRecoverSuccessfully, false); + await AssertQueueRecoveryAsync(ch, queueToRecoverWithException, false, changedQueueArguments); } finally { - _channel.QueueDelete(queueToRecoverWithException); + await _channel.QueueDeleteAsync(queueToRecoverWithException); + await ch.CloseAsync(); + await conn.CloseAsync(); + ch.Dispose(); + conn.Dispose(); } } [Fact] - public void TestTopologyRecoveryExchangeExceptionHandler() + public async Task TestTopologyRecoveryExchangeExceptionHandler() { - var latch = new ManualResetEventSlim(false); + var tcs = new TaskCompletionSource(); var exceptionHandler = new TopologyRecoveryExceptionHandler { @@ -317,51 +362,57 @@ public void TestTopologyRecoveryExchangeExceptionHandler() && ex is OperationInterruptedException operationInterruptedException && operationInterruptedException.ShutdownReason.ReplyCode == Constants.PreconditionFailed; }, - ExchangeRecoveryExceptionHandler = (re, ex, connection) => + ExchangeRecoveryExceptionHandlerAsync = async (re, ex, connection) => { - using (IChannel channel = connection.CreateChannel()) + using (IChannel channel = await connection.CreateChannelAsync()) { - channel.ExchangeDeclare(re.Name, "topic", false, false); + await channel.ExchangeDeclareAsync(re.Name, "topic", false, false); + await channel.CloseAsync(); } } }; - using AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(exceptionHandler); - conn.RecoverySucceeded += (source, ea) => latch.Set(); + AutorecoveringConnection conn = await CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandlerAsync(exceptionHandler); + conn.RecoverySucceeded += (source, ea) => tcs.SetResult(true); string exchangeToRecoverWithException = "recovery.exception.exchange"; string exchangeToRecoverSuccessfully = "successfully.recovered.exchange"; - using IChannel ch = conn.CreateChannel(); - ch.ExchangeDeclare(exchangeToRecoverWithException, "direct", false, false); - ch.ExchangeDeclare(exchangeToRecoverSuccessfully, "direct", false, false); + IChannel ch = await conn.CreateChannelAsync(); + await ch.ExchangeDeclareAsync(exchangeToRecoverWithException, "direct", false, false); + await ch.ExchangeDeclareAsync(exchangeToRecoverSuccessfully, "direct", false, false); - _channel.ExchangeDelete(exchangeToRecoverSuccessfully); - _channel.ExchangeDelete(exchangeToRecoverWithException); - _channel.ExchangeDeclare(exchangeToRecoverWithException, "topic", false, false); + await _channel.ExchangeDeleteAsync(exchangeToRecoverSuccessfully); + await _channel.ExchangeDeleteAsync(exchangeToRecoverWithException); + await _channel.ExchangeDeclareAsync(exchangeToRecoverWithException, "topic", false, false); try { - CloseAndWaitForRecovery(conn); - Wait(latch, "recovery succeeded"); + await CloseAndWaitForRecoveryAsync(conn); + await WaitAsync(tcs, "recovery succeeded"); Assert.True(_channel.IsOpen); - AssertExchangeRecovery(_channel, exchangeToRecoverSuccessfully); - AssertExchangeRecovery(_channel, exchangeToRecoverWithException); + await AssertExchangeRecoveryAsync(_channel, exchangeToRecoverSuccessfully); + await AssertExchangeRecoveryAsync(_channel, exchangeToRecoverWithException); } finally { - _channel.ExchangeDelete(exchangeToRecoverWithException); + await _channel.ExchangeDeleteAsync(exchangeToRecoverWithException); + + await ch.CloseAsync(); + await conn.CloseAsync(); + ch.Dispose(); + conn.Dispose(); } } [Fact] - public void TestTopologyRecoveryBindingExceptionHandler() + public async Task TestTopologyRecoveryBindingExceptionHandler() { - var latch = new ManualResetEventSlim(false); + var connectionRecoveryTcs = new TaskCompletionSource(); - string exchange = "topology.recovery.exchange"; - string queueWithExceptionBinding = "recovery.exception.queue"; - string bindingToRecoverWithException = "recovery.exception.binding"; + const string exchange = "topology.recovery.exchange"; + const string queueWithExceptionBinding = "recovery.exception.queue"; + const string bindingToRecoverWithException = "recovery.exception.binding"; var exceptionHandler = new TopologyRecoveryExceptionHandler { @@ -371,48 +422,54 @@ public void TestTopologyRecoveryBindingExceptionHandler() && ex is OperationInterruptedException operationInterruptedException && operationInterruptedException.ShutdownReason.ReplyCode == Constants.NotFound; }, - BindingRecoveryExceptionHandler = (b, ex, connection) => + BindingRecoveryExceptionHandlerAsync = async (b, ex, connection) => { - using (IChannel channel = connection.CreateChannel()) + using (IChannel channel = await connection.CreateChannelAsync()) { - channel.QueueDeclare(queueWithExceptionBinding, false, false, false, null); - channel.QueueBind(queueWithExceptionBinding, exchange, bindingToRecoverWithException); + await channel.QueueDeclareAsync(queueWithExceptionBinding, false, false, false); + await channel.QueueBindAsync(queueWithExceptionBinding, exchange, bindingToRecoverWithException); + await channel.CloseAsync(); } } }; - using AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(exceptionHandler); - conn.RecoverySucceeded += (source, ea) => latch.Set(); - using IChannel ch = conn.CreateChannel(); + AutorecoveringConnection conn = await CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandlerAsync(exceptionHandler); + conn.RecoverySucceeded += (source, ea) => connectionRecoveryTcs.SetResult(true); + IChannel ch = await conn.CreateChannelAsync(); - string queueWithRecoveredBinding = "successfully.recovered.queue"; - string bindingToRecoverSuccessfully = "successfully.recovered.binding"; + const string queueWithRecoveredBinding = "successfully.recovered.queue"; + const string bindingToRecoverSuccessfully = "successfully.recovered.binding"; - _channel.QueueDeclare(queueWithExceptionBinding, false, false, false, null); + await _channel.QueueDeclareAsync(queueWithExceptionBinding, false, false, false); - ch.ExchangeDeclare(exchange, "direct"); - ch.QueueDeclare(queueWithRecoveredBinding, false, false, false, null); - ch.QueueBind(queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully); - ch.QueueBind(queueWithExceptionBinding, exchange, bindingToRecoverWithException); - ch.QueuePurge(queueWithRecoveredBinding); - ch.QueuePurge(queueWithExceptionBinding); + await ch.ExchangeDeclareAsync(exchange, "direct"); + await ch.QueueDeclareAsync(queueWithRecoveredBinding, false, false, false); + await ch.QueueBindAsync(queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully); + await ch.QueueBindAsync(queueWithExceptionBinding, exchange, bindingToRecoverWithException); + await ch.QueuePurgeAsync(queueWithRecoveredBinding); + await ch.QueuePurgeAsync(queueWithExceptionBinding); - _channel.QueueUnbind(queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully); - _channel.QueueUnbind(queueWithExceptionBinding, exchange, bindingToRecoverWithException); - _channel.QueueDelete(queueWithExceptionBinding); + await _channel.QueueUnbindAsync(queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully); + await _channel.QueueUnbindAsync(queueWithExceptionBinding, exchange, bindingToRecoverWithException); + await _channel.QueueDeleteAsync(queueWithExceptionBinding); - CloseAndWaitForRecovery(conn); - Wait(latch, "recovery succeeded"); + await CloseAndWaitForRecoveryAsync(conn); + await WaitAsync(connectionRecoveryTcs, "recovery succeeded"); Assert.True(ch.IsOpen); - Assert.True(SendAndConsumeMessage(conn, queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully)); - Assert.True(SendAndConsumeMessage(conn, queueWithExceptionBinding, exchange, bindingToRecoverWithException)); + Assert.True(await SendAndConsumeMessageAsync(conn, queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully)); + Assert.True(await SendAndConsumeMessageAsync(conn, queueWithExceptionBinding, exchange, bindingToRecoverWithException)); + + await ch.CloseAsync(); + await conn.CloseAsync(); + ch.Dispose(); + conn.Dispose(); } [Fact] - public void TestTopologyRecoveryConsumerExceptionHandler() + public async Task TestTopologyRecoveryConsumerExceptionHandler() { - var latch = new ManualResetEventSlim(false); + var connectionRecoveryTcs = new TaskCompletionSource(); string queueWithExceptionConsumer = "recovery.exception.queue"; @@ -424,11 +481,12 @@ public void TestTopologyRecoveryConsumerExceptionHandler() && ex is OperationInterruptedException operationInterruptedException && operationInterruptedException.ShutdownReason.ReplyCode == Constants.NotFound; }, - ConsumerRecoveryExceptionHandler = (c, ex, connection) => + ConsumerRecoveryExceptionHandlerAsync = async (c, ex, connection) => { - using (IChannel channel = connection.CreateChannel()) + using (IChannel channel = await connection.CreateChannelAsync()) { - channel.QueueDeclare(queueWithExceptionConsumer, false, false, false, null); + await channel.QueueDeclareAsync(queueWithExceptionConsumer, false, false, false); + await channel.CloseAsync(); } // So topology recovery runs again. This time he missing queue should exist, making @@ -437,39 +495,46 @@ public void TestTopologyRecoveryConsumerExceptionHandler() } }; - using AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(exceptionHandler); - conn.RecoverySucceeded += (source, ea) => latch.Set(); - using IChannel ch = conn.CreateChannel(); - ch.ConfirmSelect(); + AutorecoveringConnection conn = await CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandlerAsync(exceptionHandler); + conn.RecoverySucceeded += (source, ea) => connectionRecoveryTcs.SetResult(true); + IChannel ch = await conn.CreateChannelAsync(); + try + { + await ch.ConfirmSelectAsync(); - _channel.QueueDeclare(queueWithExceptionConsumer, false, false, false, null); - _channel.QueuePurge(queueWithExceptionConsumer); + await _channel.QueueDeclareAsync(queueWithExceptionConsumer, false, false, false); + await _channel.QueuePurgeAsync(queueWithExceptionConsumer); - var recoverLatch = new ManualResetEventSlim(false); - var consumerToRecover = new EventingBasicConsumer(ch); - consumerToRecover.Received += (source, ea) => recoverLatch.Set(); - ch.BasicConsume(queueWithExceptionConsumer, true, "exception.consumer", consumerToRecover); + var recoveredConsumerReceivedTcs = new ManualResetEventSlim(false); + var consumerToRecover = new EventingBasicConsumer(ch); + consumerToRecover.Received += (source, ea) => recoveredConsumerReceivedTcs.Set(); + await ch.BasicConsumeAsync(queueWithExceptionConsumer, true, "exception.consumer", consumerToRecover); - _channel.QueueDelete(queueWithExceptionConsumer); + await _channel.QueueDeleteAsync(queueWithExceptionConsumer); - CloseAndWaitForShutdown(conn); - Wait(latch, TimeSpan.FromSeconds(20), "recovery succeeded"); + await CloseAndWaitForShutdownAsync(conn); + await WaitAsync(connectionRecoveryTcs, TimeSpan.FromSeconds(20), "recovery succeeded"); - Assert.True(ch.IsOpen); + Assert.True(ch.IsOpen); - ch.BasicPublish("", queueWithExceptionConsumer, _encoding.GetBytes("test message")); + await ch.BasicPublishAsync("", queueWithExceptionConsumer, _encoding.GetBytes("test message")); - Assert.True(recoverLatch.Wait(TimeSpan.FromSeconds(5))); + Assert.True(recoveredConsumerReceivedTcs.Wait(TimeSpan.FromSeconds(5))); - try - { - ch.BasicConsume(queueWithExceptionConsumer, true, "exception.consumer", consumerToRecover); + await ch.BasicConsumeAsync(queueWithExceptionConsumer, true, "exception.consumer", consumerToRecover); Assert.Fail("Expected an exception"); } catch (OperationInterruptedException e) { AssertShutdownError(e.ShutdownReason, 530); // NOT_ALLOWED - not allowed to reuse consumer tag } + finally + { + await ch.CloseAsync(); + await conn.CloseAsync(); + ch.Dispose(); + conn.Dispose(); + } } } } diff --git a/projects/Test/Unit/TestIEndpointResolverExtensions.cs b/projects/Test/Unit/TestIEndpointResolverExtensions.cs index 02bcad74cd..953776b668 100644 --- a/projects/Test/Unit/TestIEndpointResolverExtensions.cs +++ b/projects/Test/Unit/TestIEndpointResolverExtensions.cs @@ -66,7 +66,7 @@ public async Task SelectOneShouldReturnDefaultWhenThereAreNoEndpoints() { var ep = new TestEndpointResolver(new List()); - static Task selector(AmqpTcpEndpoint ep, CancellationToken ct) + Task selector(AmqpTcpEndpoint ep0, CancellationToken ct) { return Task.FromResult(null); } @@ -79,7 +79,7 @@ public async Task SelectOneShouldRaiseThrownExceptionWhenThereAreOnlyInaccessibl { var ep = new TestEndpointResolver(new List { new AmqpTcpEndpoint() }); - static Task selector(AmqpTcpEndpoint ep, CancellationToken ct) + Task selector(AmqpTcpEndpoint ep0, CancellationToken ct) { return Task.FromException(new TestEndpointException("bananas")); } @@ -99,9 +99,9 @@ public async Task SelectOneShouldReturnFoundEndpoint() { var ep = new TestEndpointResolver(new List { new AmqpTcpEndpoint() }); - static Task selector(AmqpTcpEndpoint ep, CancellationToken ct) + Task selector(AmqpTcpEndpoint ep0, CancellationToken ct) { - return Task.FromResult(ep); + return Task.FromResult(ep0); } Assert.NotNull(await ep.SelectOneAsync(selector, CancellationToken.None)); diff --git a/projects/Test/Unit/TestTimerBasedCredentialRefresher.cs b/projects/Test/Unit/TestTimerBasedCredentialRefresher.cs index 367f7caa7c..adbc1a0802 100644 --- a/projects/Test/Unit/TestTimerBasedCredentialRefresher.cs +++ b/projects/Test/Unit/TestTimerBasedCredentialRefresher.cs @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------- using System; -using System.Threading; +using System.Threading.Tasks; using RabbitMQ.Client; using Xunit; using Xunit.Abstractions; @@ -108,7 +108,7 @@ public TestTimerBasedCredentialsRefresher(ITestOutputHelper testOutputHelper) [Fact] public void TestRegister() { - ICredentialsRefresher.NotifyCredentialRefreshed cb = (bool unused) => { }; + Task cb(bool unused) => Task.CompletedTask; ICredentialsProvider credentialsProvider = new MockCredentialsProvider(_testOutputHelper); Assert.True(credentialsProvider == _refresher.Register(credentialsProvider, cb)); @@ -119,7 +119,7 @@ public void TestRegister() public void TestDoNotRegisterWhenHasNoExpiry() { ICredentialsProvider credentialsProvider = new MockCredentialsProvider(_testOutputHelper, TimeSpan.Zero); - ICredentialsRefresher.NotifyCredentialRefreshed cb = (bool unused) => { }; + Task cb(bool unused) => Task.CompletedTask; _refresher.Register(credentialsProvider, cb); @@ -127,22 +127,24 @@ public void TestDoNotRegisterWhenHasNoExpiry() } [Fact] - public void TestRefreshToken() + public async Task TestRefreshToken() { - var cbevt = new ManualResetEvent(false); + var cbtcs = new TaskCompletionSource(); bool? callbackArg = null; var credentialsProvider = new MockCredentialsProvider(_testOutputHelper, TimeSpan.FromSeconds(1)); - ICredentialsRefresher.NotifyCredentialRefreshed cb = (bool arg) => + Task cb(bool arg) { callbackArg = arg; - cbevt.Set(); - }; + cbtcs.SetResult(true); + return Task.CompletedTask; + } try { _refresher.Register(credentialsProvider, cb); - Assert.True(cbevt.WaitOne()); + await cbtcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + Assert.True(await cbtcs.Task); Assert.True(credentialsProvider.RefreshCalled); Assert.True(callbackArg); @@ -154,16 +156,17 @@ public void TestRefreshToken() } [Fact] - public void TestRefreshTokenFailed() + public async Task TestRefreshTokenFailed() { - var cbevt = new ManualResetEvent(false); + var cbtcs = new TaskCompletionSource(); bool? callbackArg = null; var credentialsProvider = new MockCredentialsProvider(_testOutputHelper, TimeSpan.FromSeconds(1)); - ICredentialsRefresher.NotifyCredentialRefreshed cb = (bool arg) => + Task cb(bool arg) { callbackArg = arg; - cbevt.Set(); - }; + cbtcs.SetResult(true); + return Task.CompletedTask; + } var ex = new Exception(); credentialsProvider.PasswordThrows(ex); @@ -171,7 +174,8 @@ public void TestRefreshTokenFailed() try { _refresher.Register(credentialsProvider, cb); - Assert.True(cbevt.WaitOne()); + await cbtcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + Assert.True(await cbtcs.Task); Assert.True(credentialsProvider.RefreshCalled); Assert.False(callbackArg); diff --git a/projects/Test/Unit/Unit.csproj b/projects/Test/Unit/Unit.csproj index 7abcf04af3..593e1d5e53 100644 --- a/projects/Test/Unit/Unit.csproj +++ b/projects/Test/Unit/Unit.csproj @@ -11,9 +11,8 @@ ../../rabbit.snk true - latest - 7.0 true + 7.3 @@ -23,8 +22,8 @@ - - + +