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());
            }
        }
Exemple #2
0
        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);
        }
Exemple #4
0
        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();
        }
Exemple #7
0
        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());
         }
     }
 }
Exemple #12
0
        /// <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));
        }
Exemple #13
0
        /// <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));
        }
Exemple #14
0
        /// <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);
        }
Exemple #16
0
        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}");
            }
        }
Exemple #18
0
 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();
        }
Exemple #20
0
 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;
     }
 }
Exemple #21
0
        /// <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());
        }
Exemple #23
0
        /// <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);
        }
Exemple #25
0
        /// <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());
        }