private async Task SendAsync(ICommand command, bool requireAcknowledgement) { try { var stopwatch = new Stopwatch(); stopwatch.Start(); if (connection.IsOpen == false) { lock (this) { if (connection.IsOpen == false) { var factory = new ConnectionFactory() { HostName = host }; this.connection = factory.CreateConnection(); _ = Log.TraceAsync($"Sender Reconnected: {host}"); } } } var channel = connection.CreateModel(); string[][] claims = null; if (Thread.CurrentPrincipal is ClaimsPrincipal principal) { claims = principal.Claims.Select(x => new string[] { x.Type, x.Value }).ToArray(); } var rabbitMessage = new RabbitMQCommandMessage() { Message = command, Claims = claims }; var body = RabbitMQCommon.Serialize(rabbitMessage); if (encryptionKey != null) { body = SymmetricEncryptor.Encrypt(encryptionAlgorithm, encryptionKey, body); } var exchange = command.GetType().GetNiceName(); var properties = channel.CreateBasicProperties(); EventingBasicConsumer consumer = null; string consumerTag = null; string correlationId = null; if (requireAcknowledgement) { string replyQueueName = channel.QueueDeclare().QueueName; consumer = new EventingBasicConsumer(channel); consumerTag = channel.BasicConsume(replyQueueName, false, consumer); correlationId = Guid.NewGuid().ToString(); properties.ReplyTo = replyQueueName; properties.CorrelationId = correlationId; } if (encryptionKey != null) { var messageHeaders = new Dictionary <string, object>(); messageHeaders.Add("Encryption", true); properties.Headers = messageHeaders; } channel.BasicPublish(exchange, String.Empty, properties, body); _ = Log.TraceAsync($"Sent{(requireAcknowledgement ? " Await" : null)}: {exchange}"); if (requireAcknowledgement) { Exception exception = null; var syncEvent = new SemaphoreSlim(0, 1); consumer.Received += (sender, e) => { try { if (e.BasicProperties.CorrelationId != correlationId) { throw new Exception("ACK response CorrelationIds should be single and unique"); } channel.BasicCancel(consumerTag); byte[] acknowledgementBody = e.Body; if (encryptionKey != null) { acknowledgementBody = SymmetricEncryptor.Decrypt(encryptionAlgorithm, encryptionKey, acknowledgementBody); } var affirmation = RabbitMQCommon.Deserialize <Acknowledgement>(acknowledgementBody); stopwatch.Stop(); if (!affirmation.Success) { _ = Log.TraceAsync($"Await Failed: {exchange}: {affirmation.ErrorMessage} {stopwatch.ElapsedMilliseconds}"); } else { _ = Log.TraceAsync($"Await Success: {exchange} {stopwatch.ElapsedMilliseconds}"); } if (!affirmation.Success) { exception = new AcknowledgementException(affirmation, exchange); } } catch (Exception ex) { exception = ex; } finally { syncEvent.Release(); } }; await syncEvent.WaitAsync(); syncEvent.Dispose(); if (exception != null) { throw exception; } } channel.Close(); channel.Dispose(); } catch (Exception ex) { _ = Log.ErrorAsync(null, ex); throw; } }
private async Task ListeningThread(IConnection connection, Func <IEvent, Task> handlerAsync) { canceller = new CancellationTokenSource(); retry: try { if (this.channel != null) { throw new Exception("Exchange already open"); } this.channel = connection.CreateModel(); this.channel.ExchangeDeclare(this.exchange, "fanout"); var queueName = this.channel.QueueDeclare().QueueName; this.channel.QueueBind(queueName, this.exchange, String.Empty); var consumer = new AsyncEventingBasicConsumer(this.channel); consumer.Received += async(sender, e) => { bool isEncrypted = e.BasicProperties.Headers != null && e.BasicProperties.Headers.Keys.Contains("Encryption") == true; var stopwatch = new Stopwatch(); stopwatch.Start(); var properties = e.BasicProperties; var acknowledgment = new Acknowledgement(); if (!isEncrypted && encryptionKey != null) { acknowledgment.Success = false; acknowledgment.ErrorMessage = "Encryption Required"; } else { try { byte[] body = e.Body; if (isEncrypted) { body = SymmetricEncryptor.Decrypt(encryptionAlgorithm, encryptionKey, e.Body); } var rabbitMessage = RabbitMQCommon.Deserialize <RabbitMQEventMessage>(body); if (rabbitMessage.Claims != null) { var claimsIdentity = new ClaimsIdentity(rabbitMessage.Claims.Select(x => new Claim(x[0], x[1])), "CQRS"); System.Threading.Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentity); } await handlerAsync(rabbitMessage.Message); stopwatch.Stop(); _ = Log.TraceAsync($"Received: {e.Exchange} {stopwatch.ElapsedMilliseconds}"); acknowledgment.Success = true; } catch (Exception ex) { stopwatch.Stop(); ex = ex.GetBaseException(); acknowledgment.Success = false; acknowledgment.ErrorMessage = ex.Message; _ = Log.TraceAsync($"Error: Received: {e.Exchange} {acknowledgment.ErrorMessage} {stopwatch.ElapsedMilliseconds}"); _ = Log.ErrorAsync(null, ex); } } }; this.channel.BasicConsume(queueName, false, consumer); } catch (Exception ex) { _ = Log.ErrorAsync(ex); if (!canceller.IsCancellationRequested) { if (channel != null) { channel.Close(); channel.Dispose(); channel = null; } await Task.Delay(retryDelay); goto retry; } } canceller.Dispose(); canceller = null; if (channel != null) { channel.Close(); channel.Dispose(); channel = null; } IsOpen = false; }