private void TryCreateIndex(IndexKeysDefinition <MessageJournalEntryDocument> indexKeys, string name = null) { var options = new CreateIndexOptions { Name = name }; if (_collationSupported) { options.Collation = _collation; } try { _messageJournalEntries.Indexes.CreateOne(indexKeys, options); _diagnosticService.Emit(new MongoDBEventBuilder(this, MongoDBEventType.IndexCreated) { DatabaseName = _messageJournalEntries.Database.DatabaseNamespace.DatabaseName, CollectionName = _messageJournalEntries.CollectionNamespace.CollectionName, IndexName = name ?? indexKeys.ToString() }.Build()); } catch (Exception e) { _diagnosticService.Emit(new MongoDBEventBuilder(this, MongoDBEventType.IndexCreationFailed) { DatabaseName = _messageJournalEntries.Database.DatabaseNamespace.DatabaseName, CollectionName = _messageJournalEntries.CollectionNamespace.CollectionName, IndexName = name ?? indexKeys.ToString(), Exception = e }.Build()); } }
private void CloseManagedConnection(ManagedConnection connection) { if (connection == null) { return; } try { connection.CloseManagedConnection(true); return; } catch (Exception ex) { _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQConnectionError) { Detail = "Unhandled exception closing managed connection ID " + connection.ManagedConnectionId, Exception = ex }.Build()); } try { connection.Abort(); } catch (Exception ex) { _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQConnectionError) { Detail = "Unhandled exception aborting managed connection ID " + connection.ManagedConnectionId, Exception = ex }.Build()); } }
private async Task <MessageJournalReadResult> ReadNext(CancellationToken cancellationToken, MessageJournalPosition current) { MessageJournalReadResult readResult = null; try { readResult = await _messageJournal.Read(current, _batchSize, _filter, cancellationToken); } catch (OperationCanceledException) { throw; } catch (Exception ex) { _diagnosticService.Emit(new DiagnosticEventBuilder(this, DiagnosticEventType.UnhandledException) { Detail = "Error reading message journal", Exception = ex }.Build()); if (_rethrowExceptions) { throw; } } return(readResult); }
public DbConnection GetConnection() { CheckDisposed(); var myConnection = _connection; if (myConnection != null && myConnection.State == ConnectionState.Open) { return(myConnection); } lock (_syncRoot) { if (myConnection != null && myConnection.State == ConnectionState.Broken) { try { myConnection.Close(); _diagnosticService.Emit(new SQLEventBuilder(this, SQLEventType.SQLConnectionClosed) { ConnectionName = _connectionStringSettings.Name }.Build()); } catch (Exception ex) { _diagnosticService.Emit(new SQLEventBuilder(this, SQLEventType.SQLCommandError) { Detail = "Error closing singleton connection", ConnectionName = _connectionStringSettings.Name, Exception = ex }.Build()); } myConnection = null; } if (myConnection == null) { myConnection = _dbProviderFactory.CreateConnection(); if (myConnection != null) { myConnection.ConnectionString = _connectionStringSettings.ConnectionString; } } if (myConnection != null && myConnection.State == ConnectionState.Closed) { myConnection.Open(); _diagnosticService.Emit(new SQLEventBuilder(this, SQLEventType.SQLConnectionOpened) { ConnectionName = _connectionStringSettings.Name }.Build()); } _connection = myConnection; } return(myConnection); }
/// <summary> /// Initializes a new <see cref="MulticastSubscriptionTrackingService"/> decorating the /// specified <paramref name="inner"/> <see cref="ISubscriptionTrackingService"/> /// configured to broadcast and consume changes over the multicast group at the specified /// <paramref name="groupAddress"/>. /// </summary> /// <param name="inner">The underlying subscription tracking service used to persist or /// maintain subscription state</param> /// <param name="groupAddress">The address of the multicast group to join</param> /// <param name="port">The local port to bind to</param> /// <param name="diagnosticService">(Optional) The service through which diagnostic events /// are reported and processed</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="inner"/> or /// <paramref name="groupAddress"/> is <c>null</c></exception> /// <exception cref="ArgumentException">Thrown if <paramref name="groupAddress"/> does not /// specify a compatible <see cref="AddressFamily"/></exception> /// <exception cref="SocketException">Thrown if there is an error joining the multicast /// group, i.e. due to an invalid multicast address.</exception> /// <remarks> /// The multicast group <paramref name="groupAddress"/> must be on the 224.0.0/4 subnet /// </remarks> public MulticastSubscriptionTrackingService(ISubscriptionTrackingService inner, IPAddress groupAddress, int port, IDiagnosticService diagnosticService = null) { _nodeId = NodeId.Generate(); _inner = inner ?? throw new ArgumentNullException(nameof(inner)); _groupAddress = groupAddress ?? throw new ArgumentNullException(nameof(groupAddress)); _port = port; _diagnosticService = diagnosticService ?? DiagnosticService.DefaultInstance; _broadcastClient = new UdpClient { ExclusiveAddressUse = false }; _broadcastClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); _broadcastClient.JoinMulticastGroup(groupAddress, IPAddress.Any); _broadcastClient.Ttl = 2; _broadcastClient.Client.Bind(new IPEndPoint(IPAddress.Any, 0)); var broadcastBinding = (IPEndPoint)_broadcastClient.Client.LocalEndPoint; _diagnosticService.Emit(new MulticastEventBuilder(this, MulticastEventType.BroadcastSocketBound) { Node = _nodeId.ToString(), Host = broadcastBinding.Address.ToString(), Port = broadcastBinding.Port }.Build()); _listenerClient = new UdpClient { ExclusiveAddressUse = false }; _listenerClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); _listenerClient.JoinMulticastGroup(groupAddress, IPAddress.Any); _listenerClient.Client.Bind(new IPEndPoint(IPAddress.Any, _port)); var listenerBinding = (IPEndPoint)_listenerClient.Client.LocalEndPoint; _diagnosticService.Emit(new MulticastEventBuilder(this, MulticastEventType.ListenerSocketBound) { Node = _nodeId.ToString(), Host = listenerBinding.Address.ToString(), Port = listenerBinding.Port }.Build()); _cancellationTokenSource = new CancellationTokenSource(); _receiveResultQueue = new ActionBlock <UdpReceiveResult>(HandleReceiveResult, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2 }); _listenTask = Task.Run(async() => await Listen(_cancellationTokenSource.Token)); _diagnosticService.Emit(new MulticastEventBuilder(this, MulticastEventType.ListenerStarted) { Node = _nodeId.ToString(), Host = listenerBinding.Address.ToString(), Port = listenerBinding.Port }.Build()); }
/// <summary> /// Called by the <see cref="Dispose()"/> method or finalizer to ensure that /// resources are released /// </summary> /// <param name="disposing">Indicates whether this method is called from the /// <see cref="Dispose()"/> method (<c>true</c>) or the finalizer (<c>false</c>)</param> /// <remarks> /// This method will not be called more than once /// </remarks> protected virtual void Dispose(bool disposing) { _httpListener.Stop(); _cancellationTokenSource.Cancel(); _listenTask.Wait(TimeSpan.FromSeconds(30)); if (!disposing) { return; } _cancellationTokenSource.Dispose(); try { _httpListener.Close(); _diagnosticService.Emit( new HttpEventBuilder(this, HttpEventType.HttpServerStopped) { Uri = _baseUri }.Build()); } catch (Exception ex) { _diagnosticService.Emit( new HttpEventBuilder(this, HttpEventType.HttpServerError) { Detail = "Unexpected error closing HTTP listener", Uri = _baseUri, Exception = ex }.Build()); _httpListener.Abort(); } _bus.Dispose(); TransportService.Dispose(); if (_messageQueueingService is IDisposable disposableMessageQueueingService) { disposableMessageQueueingService.Dispose(); } if (_messageJournal is IDisposable disposableMessageJournal) { disposableMessageJournal.Dispose(); } if (_subscriptionTrackingService is IDisposable disposableSubscriptionTrackingService) { disposableSubscriptionTrackingService.Dispose(); } _metricsCollector.Dispose(); }
public void Init() { var cancellationToken = _cancellationTokenSource.Token; _channel = CreateChannel(cancellationToken); var consumer = new EventingBasicConsumer(_channel); consumer.Received += (sender, args) => { try { _consume(_channel, args, cancellationToken); } catch (Exception ex) { _diagnosticService.Emit(new RabbitMQEventBuilder(this, DiagnosticEventType.MessageNotAcknowledged) { Detail = "Unhandled exception in consumer callback", Exception = ex, ChannelNumber = _channel.ChannelNumber, ConsumerTag = _consumerTag, DeliveryTag = args.DeliveryTag, }.Build()); _channel.BasicNack(args.DeliveryTag, true, false); } }; _channel.BasicConsume(_queueName, _autoAcknowledge, _consumerTag, consumer); _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQConsumerAdded) { ChannelNumber = _channel.ChannelNumber, ConsumerTag = _consumerTag }.Build()); }
/// <summary> /// Handles an exception by recording exception details in the log and updating /// the HTTP response with the appropriate status code /// </summary> /// <param name="ex">The exception to handle</param> /// <returns>Returns <c>true</c> if the exception was successfully handled; /// <c>false</c> otherwise</returns> /// <remarks> /// This method always returns <c>true</c>. The return value is provided in order to /// match the signature of the <see cref="AggregateException.Handle"/> method. /// </remarks> public bool HandleException(Exception ex) { if (ex is AggregateException aggregateException) { aggregateException.Handle(HandleException); return(true); } if (ex is UnauthorizedAccessException unauthorizedAccessException) { _response.StatusCode = 401; _diagnosticService.Emit(new HttpEventBuilder(_source, DiagnosticEventType.AccessDenied) { Detail = "Unauthorized", Uri = _request.Url, Method = _request.HttpMethod, Status = 401, Exception = unauthorizedAccessException }.Build()); return(true); } if (ex is MessageNotAcknowledgedException notAcknowledgedException) { // HTTP 422: Unprocessable Entity _response.StatusCode = 422; _diagnosticService.Emit(new HttpEventBuilder(_source, DiagnosticEventType.MessageNotAcknowledged) { Detail = "Message not acknowledged", Uri = _request.Url, Method = _request.HttpMethod, Status = 422, Exception = notAcknowledgedException }.Build()); return(true); } // HTTP 500: Unknown error _response.StatusCode = 500; _diagnosticService.Emit(new HttpEventBuilder(_source, DiagnosticEventType.UnhandledException) { Detail = "Unexpected error", Uri = _request.Url, Method = _request.HttpMethod, Status = 500, Exception = ex }.Build()); return(true); }
private async Task HandleReceiveResult(UdpReceiveResult result) { try { var datagram = SubscriptionTrackingDatagram.Decode(result.Buffer); if (datagram.NodeId == _nodeId) { return; } switch (datagram.Action) { case SubscriptionTrackingDatagram.ActionType.Add: await _inner.AddSubscription(datagram.Topic, datagram.SubscriberUri, datagram.TTL); return; case SubscriptionTrackingDatagram.ActionType.Remove: await _inner.RemoveSubscription(datagram.Topic, datagram.SubscriberUri); return; default: await _diagnosticService.EmitAsync(new MulticastEventBuilder(this, MulticastEventType.MalformedDatagram) { Detail = "Unknown or unsupported subscription tracking action: " + datagram.Action, Node = _nodeId.ToString(), Host = result.RemoteEndPoint.Address.ToString(), Port = result.RemoteEndPoint.Port }.Build()); break; } } catch (Exception e) { _diagnosticService.Emit(new MulticastEventBuilder(this, MulticastEventType.MalformedDatagram) { Detail = "Error unmarshaling datagram", Node = _nodeId.ToString(), Host = result.RemoteEndPoint.Address.ToString(), Port = result.RemoteEndPoint.Port, Exception = e }.Build()); } }
/// <inheritdoc /> public Task AddSubscription(TopicName topic, Uri subscriber, TimeSpan ttl = new TimeSpan(), CancellationToken cancellationToken = new CancellationToken()) { if (topic == null) { throw new ArgumentNullException(nameof(topic)); } if (subscriber == null) { throw new ArgumentNullException(nameof(subscriber)); } try { var expires = ttl <= TimeSpan.Zero ? DateTime.MaxValue : DateTime.UtcNow.Add(ttl); var fb = Builders <SubscriptionDocument> .Filter; var filter = fb.Eq(s => s.Topic, topic.ToString()) & fb.Eq(s => s.Subscriber, subscriber.ToString()); var update = Builders <SubscriptionDocument> .Update .Set(s => s.Expires, expires); return(_subscriptions.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true }, cancellationToken)); } catch (Exception ex) { _diagnosticService.Emit(new MongoDBEventBuilder(this, MongoDBEventType.MongoDBUpdateFailed) { Detail = $"Error uperting subscription to topic {topic} for subscriber {subscriber}", CollectionName = _subscriptions.CollectionNamespace.CollectionName, DatabaseName = _subscriptions.Database.DatabaseNamespace.DatabaseName, Exception = ex, Topic = topic }.Build()); throw; } }
/// <summary> /// Reads previously queued messages from the database and initiates message processing /// </summary> /// <param name="cancellationToken">A cancellation token that can be used by the caller /// to cancel initialization</param> /// <returns>Returns a task that completes when initialization is complete</returns> public virtual async Task Init(CancellationToken cancellationToken = default(CancellationToken)) { if (Interlocked.Exchange(ref _initialized, 1) == 0) { try { await EnqueueExistingMessages(cancellationToken); } catch (Exception ex) { DiagnosticService.Emit( new DiagnosticEventBuilder(this, DiagnosticEventType.ComponentInitializationError) { Detail = "Error enqueueing previously queued message(s)", Exception = ex }.Build()); } } }
/// <summary> /// Initializes <see cref="IMessageJournalingCommandBuilders"/> /// </summary> /// <returns>Returns initialized <see cref="IMessageJournalingCommandBuilders"/></returns> public IMessageJournalingCommandBuilders InitMessageJournalingCommandBuilders() { var providerName = _connectionStringSettings.ProviderName; IMessageJournalingCommandBuildersProvider provider; if (string.IsNullOrWhiteSpace(providerName)) { _diagnosticService.Emit(new SQLEventBuilder(this, DiagnosticEventType.ConfigurationDefault) { Detail = "No provider name specified; using default provider (MSSQL)", ConnectionName = _connectionStringSettings.Name, }.Build()); provider = new MSSQLCommandBuildersProvider(); } else { provider = _providerService.GetProvider <IMessageJournalingCommandBuildersProvider>(providerName); } return(provider.GetMessageJournalingCommandBuilders(_connectionStringSettings)); }
/// <inheritdoc /> public Task <IPrincipal> Validate(string securityToken) { if (string.IsNullOrWhiteSpace(securityToken)) { throw new ArgumentNullException(nameof(securityToken)); } var tokenHandler = new JwtSecurityTokenHandler(); var signingKeys = new List <SecurityKey>(); if (_signingKey != null) { signingKeys.Add(_signingKey); } if (_fallbackSigningKey != null) { signingKeys.Add(_fallbackSigningKey); } var parameters = new TokenValidationParameters { ValidateIssuer = false, ValidateAudience = false }; if (signingKeys.Any()) { parameters.IssuerSigningKeys = signingKeys; parameters.RequireSignedTokens = true; } else { parameters.RequireSignedTokens = false; } IPrincipal principal; try { principal = tokenHandler.ValidateToken(securityToken, parameters, out SecurityToken _); } catch (Exception ex) { principal = null; _diagnosticService.Emit(new DiagnosticEventBuilder(this, DiagnosticEventType.InvalidSecurityToken) { Exception = ex }.Build()); } return(Task.FromResult(principal)); }
/// <inheritdoc /> public virtual DbConnection GetConnection() { var connection = ProviderFactory.CreateConnection(); // The abstract DbProviderFactory class specifies a virtual CreateConnection method // whose default implementation returns null. There is a possibility that this method // will not be overridden by the concrete provider factory. If that is the case, then // there is nothing we can do other than avoid a NullReferenceException by not attempting // to set the connection string or open the connection. if (connection != null) { connection.ConnectionString = ConnectionStringSettings.ConnectionString; connection.Open(); _diagnosticService.Emit(new SQLEventBuilder(this, SQLEventType.SQLConnectionOpened) { ConnectionName = ConnectionStringSettings.Name }.Build()); } return(connection); }
public void CloseManagedConnection(bool disposing) { if (_closed) { return; } _closed = true; if (disposing) { lock (_syncRoot) { _connection.Close(); } _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQConnectionClosed) { Detail = "Managed connection ID " + ManagedConnectionId + " successfully closed" }.Build()); } GC.SuppressFinalize(this); }
private void StopListening() { try { if (!_cancellationTokenSource.IsCancellationRequested) { _cancellationTokenSource.Cancel(); } if (_listenTask != null) { _listenTask.Wait(TimeSpan.FromSeconds(30)); } if (_httpListener.IsListening) { _httpListener.Stop(); } _diagnosticService.Emit( new HttpEventBuilder(this, HttpEventType.HttpServerStopped) { Uri = _baseUri }.Build()); } catch (Exception ex) { _diagnosticService.Emit( new HttpEventBuilder(this, HttpEventType.HttpServerError) { Detail = "Unexpected error closing HTTP listener", Uri = _baseUri, Exception = ex }.Build()); _httpListener.Abort(); } }
private MessageName GetSchemaTypeName(Type type) { try { return(_dataContractExporter.GetSchemaTypeName(type).ToString()); } catch (Exception ex) { _diagnosticService.Emit(new DiagnosticEventBuilder(this, DiagnosticEventType.InvalidDataContract) { Detail = $"Error determining schema type name for {type}", Exception = ex }.Build()); // Applly the default namespace convention return($"http://schemas.datacontract.org/2004/07/{type.Namespace}:{type.Name}"); } }
public async Task HandleMessage(object content, IMessageContext messageContext, CancellationToken cancellationToken) { try { var handler = _handlerFactory(); if (handler == null && !_method.IsStatic) { throw new NullReferenceException("Handler factory returned null handler instance"); } await(Task) _method.Invoke(handler, new[] { content, messageContext, cancellationToken }); } catch (Exception e) { _diagnosticService.Emit(new DiagnosticEventBuilder(null, DiagnosticEventType.HandlerActivationError) { Detail = $"Error activating instance of message handler {_handlerType}: {e.Message}", Exception = e }.Build()); throw; } }
/// <summary> /// Initializes a new <see cref="RabbitMQTransportService"/> /// </summary> /// <param name="options">The options that govern the configuration and behavior of the /// RabbitMQ transport</param> public RabbitMQTransportService(RabbitMQTransportServiceOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } _baseUri = options.BaseUri; _connectionManager = options.ConnectionManager; _diagnosticService = options.DiagnosticService ?? DiagnosticService.DefaultInstance; _messageJournal = options.MessageJournal; _securityTokenService = options.SecurityTokenService ?? new JwtSecurityTokenService(); _encoding = options.Encoding ?? Encoding.GetEncoding(RabbitMQDefaults.Encoding); _defaultQueueOptions = options.DefaultQueueOptions ?? new QueueOptions(); var connection = _connectionManager.GetConnection(_baseUri); var topics = (options.Topics ?? Enumerable.Empty <TopicName>()).Where(t => t != null); using (var channel = connection.CreateModel()) { foreach (var topicName in topics) { var exchangeName = topicName.GetTopicExchangeName(); channel.ExchangeDeclare(exchangeName, "fanout", _defaultQueueOptions.IsDurable, false, new Dictionary <string, object>()); _diagnosticService.Emit( new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQExchangeDeclared) { Detail = "Fanout exchange declared for topic", Exchange = exchangeName, Topic = topicName, ChannelNumber = channel.ChannelNumber }.Build()); } } _inboundQueue = new RabbitMQQueue(connection, InboxQueueName, this, _encoding, _defaultQueueOptions, _diagnosticService, _securityTokenService, null); _inboundQueue.Init(); }
private Task SendMessage(Message message, IEndpointCredentials credentials, CancellationToken cancellationToken) { try { return(_transportService.SendMessage(message, credentials, cancellationToken)); } catch (Exception ex) { _diagnosticService.Emit(new DiagnosticEventBuilder(this, DiagnosticEventType.MessageSendFailed) { Message = message, Exception = ex }.Build()); throw; } }
/// <summary> /// Called by the bus to indicate that a message has been received that is /// related to another message (possibly a reply) /// </summary> /// <param name="message"></param> /// <returns>Returns a task that will complete when all reply stream /// observers have been notified that a reply was received</returns> public Task NotifyReplyReceived(Message message) { CheckDisposed(); var relatedTo = message.Headers.RelatedTo; return(Task.Run(() => { if (_cache.Get(relatedTo) is ReplyStream replyStream) { var messageContent = _messageMarshaller.Unmarshal(message); replyStream.NotifyReplyReceived(messageContent); } else { _diagnosticService.Emit( new DiagnosticEventBuilder(this, DiagnosticEventType.CorrelationFailed) { Message = message, Detail = $"No sent message found with ID {relatedTo}" }.Build()); } })); }
public ManagedConnection(Uri uri, IDiagnosticService diagnosticService) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } // Trailing slashes causes errors when connecting to RabbitMQ uri = uri.WithoutTrailingSlash(); var managedConnectionIdBuilder = new UriBuilder(uri) { // Sanitize credentials in managed connection ID. This value // is output in log messages for diagnostics, so we don't want // or need the credentials displayed. UserName = "", Password = "" }; ManagedConnectionId = managedConnectionIdBuilder.Uri.ToString(); IConnectionFactory connectionFactory = new ConnectionFactory { Uri = uri, AutomaticRecoveryEnabled = true, RequestedHeartbeat = 10, NetworkRecoveryInterval = TimeSpan.FromSeconds(10) }; _diagnosticService = diagnosticService ?? throw new ArgumentNullException(nameof(diagnosticService)); _connection = connectionFactory.CreateConnection(); _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQConnectionOpened) { Detail = "Opened managed connection ID " + ManagedConnectionId }.Build()); }
/// <summary> /// Performs final initialization of the transport service /// </summary> /// <param name="cancellationToken">A cancellation token that can be used by the caller /// can use to cancel the initialization process</param> /// <returns>Returns a task that completes when initialization is complete</returns> public async Task Init(CancellationToken cancellationToken = default(CancellationToken)) { if (Interlocked.Exchange(ref _initialized, 1) == 0) { try { await _messageQueueingService.CreateQueue(_outboundQueueName, this, cancellationToken : cancellationToken); await _diagnosticService.EmitAsync( new DiagnosticEventBuilder(this, DiagnosticEventType.ComponentInitialization) { Detail = "HTTP transport service initialized" }.Build(), cancellationToken); } catch (Exception ex) { _diagnosticService.Emit( new DiagnosticEventBuilder(this, DiagnosticEventType.ComponentInitializationError) { Detail = "Error initializating outbound transport queue", Exception = ex }.Build()); throw; } } }
public async Task <Message> Decrypt(Message encryptedMessage) { var encryptedHeaders = new EncryptedMessageHeaders(encryptedMessage.Headers); var iv = Convert.FromBase64String(encryptedHeaders.IV); var headerCiphertext = Convert.FromBase64String(encryptedHeaders.Headers); var contentCiphertext = Convert.FromBase64String(encryptedMessage.Content); var signature = DecodeSignature(encryptedMessage, encryptedHeaders); var keyNumber = 0; var keyCount = _decryptionKeys.Count; var innerExceptions = new List <Exception>(); foreach (var key in _decryptionKeys) { keyNumber++; byte[] headerCleartext; byte[] contentCleartext; try { headerCleartext = await Decrypt(headerCiphertext, key, iv); } catch (Exception ex) { innerExceptions.Add(ex); _diagnosticService.Emit(new DiagnosticEventBuilder(this, DiagnosticEventType.DecryptionError) { Detail = $"Error decrypting message headers using key {keyNumber} of {keyCount}", Message = encryptedMessage, Exception = ex }.Build()); continue; } var signatureVerified = false; try { signatureVerified = Verify(key, headerCleartext, signature); if (!signatureVerified) { _diagnosticService.Emit(new DiagnosticEventBuilder(this, DiagnosticEventType.SignatureVerificationFailure) { Detail = $"Signature verification failed using key {keyNumber} of {keyCount}", Message = encryptedMessage }.Build()); } } catch (Exception ex) { innerExceptions.Add(ex); _diagnosticService.Emit(new DiagnosticEventBuilder(this, DiagnosticEventType.SignatureVerificationFailure) { Detail = $"Unexpected error verifying message signature using key {keyNumber} of {keyCount}", Message = encryptedMessage, Exception = ex }.Build()); } if (!signatureVerified) { continue; } try { contentCleartext = await Decrypt(contentCiphertext, key, iv); } catch (Exception ex) { innerExceptions.Add(ex); _diagnosticService.Emit(new DiagnosticEventBuilder(this, DiagnosticEventType.DecryptionError) { Detail = $"Error decrypting message content using key {keyNumber} of {keyCount}", Message = encryptedMessage, Exception = ex }.Build()); continue; } var headers = await UnmarshalHeaders(headerCleartext); var content = await UnmarshalContent(contentCleartext); return(new Message(headers, content)); } throw new MessageEncryptionException($"Unable to decrypt and verify message using any of {keyCount} available decryption key(s)", innerExceptions); }
/// <summary> /// Initializes RabbitMQ queues and exchanges /// </summary> public void Init() { using (var channel = _connection.CreateModel()) { var queueArgs = new Dictionary <string, object> { { "x-dead-letter-exchange", _deadLetterExchange }, }; if (_ttl > TimeSpan.Zero) { queueArgs["x-expires"] = _ttl; } channel.ExchangeDeclare(_queueExchange, "direct", _isDurable, false, null); _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQExchangeDeclared) { Detail = "Primary exchange declared for queue", Exchange = _queueExchange, Queue = _queueName }.Build()); channel.ExchangeDeclare(_deadLetterExchange, "direct", _isDurable, false, null); _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQExchangeDeclared) { Detail = "Dead letter exchange declared for queue", Exchange = _deadLetterExchange, Queue = _queueName }.Build()); channel.QueueDeclare(_queueName, _isDurable, false, false, queueArgs); _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQQueueDeclared) { Detail = "Queue declared", Queue = _queueName }.Build()); channel.QueueBind(_queueName, _queueExchange, "", null); _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQQueueBound) { Detail = "Queue bound to primary exchange", Exchange = _queueExchange, Queue = _queueName }.Build()); var retryTtlMs = (int)_retryDelay.TotalMilliseconds; var retryQueueArgs = new Dictionary <string, object> { { "x-dead-letter-exchange", _queueExchange }, { "x-message-ttl", retryTtlMs } }; channel.ExchangeDeclare(_retryExchange, "direct", _isDurable, false, null); _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQExchangeDeclared) { Detail = "Retry exchange declared for queue", Exchange = _retryExchange, Queue = _queueName }.Build()); channel.QueueDeclare(_retryQueueName, _isDurable, false, false, retryQueueArgs); _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQQueueDeclared) { Detail = "Retry queue declared", Queue = _retryQueueName }.Build()); channel.QueueBind(_retryQueueName, _retryExchange, "", null); _diagnosticService.Emit(new RabbitMQEventBuilder(this, RabbitMQEventType.RabbitMQQueueDeclared) { Detail = "Retry queue bound to retry exchange", Exchange = _retryExchange, Queue = _retryQueueName }.Build()); } _consumer.Init(); _diagnosticService.Emit(new RabbitMQEventBuilder(this, DiagnosticEventType.ComponentInitialization) { Detail = "RabbitMQ queue initialized", Queue = _queueName }.Build()); }