Example #1
1
        public void CanMeasureTimeSpentInSteps()
        {
            var stats = new PipelineStepProfilerStats();
            var pipeline = new DefaultPipeline()
                .OnReceive(new Step300())
                .OnReceive(new Step100())
                .OnReceive(new Step200());

            var profiler = new PipelineStepProfiler(pipeline, stats);

            var receivePipeline = profiler.ReceivePipeline();
            var invoker = new DefaultPipelineInvoker();
            var transportMessage = new TransportMessage(new Dictionary<string, string>(), new byte[0]);

            using (new DefaultTransactionContextScope())
            {
                var stepContext = new IncomingStepContext(transportMessage, AmbientTransactionContext.Current);

                invoker.Invoke(stepContext, receivePipeline).Wait();

                var stepStats = stats.GetStats();

                Console.WriteLine(string.Join(Environment.NewLine, stepStats));
            }
        }
Example #2
0
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var message = context.Load<Message>();

            string correlationId;

            var hasCorrelationId = message.Headers.TryGetValue(Headers.CorrelationId, out correlationId);
            if (hasCorrelationId)
            {
                var isRequestReplyCorrelationId = correlationId.StartsWith(SpecialCorrelationIdPrefix);
                if (isRequestReplyCorrelationId)
                {
                    string dummy;
                    var isRequest = message.Headers.TryGetValue(SpecialRequestTag, out dummy);

                    if (!isRequest)
                    {
                        // it's the reply!
                        _messages[correlationId] = message;
                        return;
                    }
                }
            }

            await next();
        }
Example #3
0
        void PossiblyDecompressTransportMessage(IncomingStepContext context)
        {
            var transportMessage = context.Load<TransportMessage>();

            string contentEncoding;

            if (!transportMessage.Headers.TryGetValue(Headers.ContentEncoding, out contentEncoding))
                return;

            if (contentEncoding != ZipMessagesOutgoingStep.GzipEncodingHeader)
            {
                var message = $"The message {transportMessage.GetMessageLabel()} has a '{Headers.ContentEncoding}' with the" +
                              $" value '{contentEncoding}', but this middleware only knows how to decompress" +
                              $" '{ZipMessagesOutgoingStep.GzipEncodingHeader}'";

                throw new ArgumentException(message);
            }

            var headers = transportMessage.Headers.Clone();
            var compressedBody = transportMessage.Body;

            headers.Remove(Headers.ContentEncoding);

            var body = _zipper.Unzip(compressedBody);

            context.Save(new TransportMessage(headers, body));
        }
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            await next();

            var message = context.Load<Message>();
            var handlerInvokers = context.Load<HandlerInvokers>();

            var createdAndUpdatedSagaData = handlerInvokers
                .Where(i => i.HasSaga)
                .Select(i => new
                {
                    Handler = i.Handler,
                    SagaData = i.GetSagaData()
                })
                .ToList();

            var saveTasks = createdAndUpdatedSagaData
                .Select(sagaData =>
                {
                    var metadata = GetMetadata(sagaData.SagaData, sagaData.Handler, message);

                    return _sagaSnapshotStorage.Save(sagaData.SagaData, metadata);
                });

            await Task.WhenAll(saveTasks);
        }
        /// <summary>
        /// Invokes the routing function and performs some action depending on the returned <see cref="ForwardAction"/> result
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transportMessage = context.Load<TransportMessage>();
            var routingResult = (await _routingFunction(transportMessage)) ?? ForwardAction.None;
            var actionType = routingResult.ActionType;

            switch (actionType)
            {
                case ActionType.Forward:
                    var destinationAddresses = routingResult.DestinationAddresses;
                    var transactionContext = context.Load<ITransactionContext>();

                    _log.Debug("Forwarding {0} to {1}", transportMessage.GetMessageLabel(), string.Join(", ", destinationAddresses));

                    await Task.WhenAll(
                        destinationAddresses
                            .Select(address => _transport.Send(address, transportMessage, transactionContext))
                        );
                    break;

                case ActionType.None:
                    await next();
                    break;

                default:
                    await next();
                    break;
            }
        }
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var message = context.Load<Message>();
            var headers = message.Headers;

            if (headers.ContainsKey(MapLegacyHeadersIncomingStep.LegacyMessageHeader))
            {
                var body = message.Body;
                var array = body as object[];

                if (array == null)
                {
                    throw new FormatException(
                        $"Incoming message has the '{MapLegacyHeadersIncomingStep.LegacyMessageHeader}' header, but the message body {body} is not an object[] as expected");
                }

                foreach (var bodyToDispatch in array)
                {
                    var messageBodyToDispatch = PossiblyConvertBody(bodyToDispatch, headers);

                    context.Save(new Message(headers, messageBodyToDispatch));

                    await next();
                }

                return;
            }

            await next();
        }
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var handlerInvokersForSagas = context.Load<HandlerInvokers>()
                .Where(l => l.HasSaga)
                .ToList();

            var message = context.Load<Message>();
            var messageId = message.GetMessageId();

            var transactionContext = context.Load<ITransactionContext>();

            foreach (var handlerInvoker in handlerInvokersForSagas)
            {
                var sagaData = handlerInvoker.GetSagaData() as IIdempotentSagaData;

                if (sagaData == null) continue;

                var idempotencyData = sagaData.IdempotencyData
                                      ?? (sagaData.IdempotencyData = new IdempotencyData());

                if (idempotencyData.HasAlreadyHandled(messageId))
                {
                    _log.Info("Message with ID {0} has already been handled by saga with ID {1}",
                        messageId, sagaData.Id);

                    var outgoingMessages = idempotencyData
                        .GetOutgoingMessages(messageId)
                        .ToList();

                    if (outgoingMessages.Any())
                    {
                        _log.Info("Found {0} outgoing messages to be (re-)sent... will do that now",
                            outgoingMessages.Count);

                        foreach (var messageToResend in outgoingMessages)
                        {
                            foreach (var destinationAddress in messageToResend.DestinationAddresses)
                            {
                                await
                                    _transport.Send(destinationAddress, messageToResend.TransportMessage,
                                        transactionContext);
                            }
                        }
                    }
                    else
                    {
                        _log.Info("Found no outgoing messages to be (re-)sent...");
                    }

                    handlerInvoker.SkipInvocation();
                }
                else
                {
                    idempotencyData.MarkMessageAsHandled(messageId);
                }
            }

            await next();
        }
 public async Task Process(IncomingStepContext context, Func<Task> next)
 {
     using (var scope = new TransactionScope(ScopeOption, _transactionOptions, AsyncFlowOption))
     {
         await next();
         scope.Complete();
     }
 }
            public async Task Process(IncomingStepContext context, Func<Task> next)
            {
                GetActionList(context).Add(string.Format("enter {0}", _name));

                await next();

                GetActionList(context).Add(string.Format("leave {0}", _name));
            }
Example #10
0
            public async Task Process(IncomingStepContext context, Func<Task> next)
            {
                var statsContext = context.Load<StatsContext>();

                using (statsContext.Measure(_nextStep))
                {
                    await next();
                }
            }
        void MutateLegacyTransportMessage(IncomingStepContext context, Dictionary<string, string> headers, TransportMessage transportMessage)
        {
            var newHeaders = MapTrivialHeaders(headers);

            MapSpecialHeaders(newHeaders);

            newHeaders[LegacyMessageHeader] = "";

            context.Save(new TransportMessage(newHeaders, transportMessage.Body));
        }
Example #12
0
 /// <summary>
 /// Reorders the handler invokers if necessary
 /// </summary>
 public async Task Process(IncomingStepContext context, Func<Task> next)
 {
     var handlerInvokers = context.Load<HandlerInvokers>();
     var orderedHandlerInvokers = handlerInvokers.OrderBy(i => _configuration.GetIndex(i.Handler));
     var newHandlerInvokers = new HandlerInvokers(handlerInvokers.Message, orderedHandlerInvokers);
     
     context.Save(newHandlerInvokers);
     
     await next();
 }
        /// <summary>
        /// Carries out whichever logic it takes to do something good for the incoming message :)
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var message = context.Load<Message>();
            var newPrincipal = GetClaimsPrincipalOrNull(message);

            using (new CurrentPrincipalRewriter(newPrincipal))
            {
                await next();
            }
        }
Example #14
0
            public async Task Process(IncomingStepContext context, Func<Task> next)
            {
                var statsContext = new StatsContext();

                // save stats context for all the ProfilerSteps to find
                context.Save(statsContext);

                await next();

                _profilerStats.Register(statsContext);
            }
        /// <summary>
        /// Executes the entire message processing pipeline in an exception handler, tracking the number of failed delivery attempts.
        /// Forwards the message to the error queue when the max number of delivery attempts has been exceeded.
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transportMessage = context.Load<TransportMessage>();
            var transactionContext = context.Load<ITransactionContext>();
            var messageId = transportMessage.Headers.GetValueOrNull(Headers.MessageId);

            if (string.IsNullOrWhiteSpace(messageId))
            {
                await MoveMessageToErrorQueue("<no message ID>", transportMessage,
                    transactionContext, string.Format("Received message with empty or absent '{0}' header! All messages must be" +
                                                      " supplied with an ID . If no ID is present, the message cannot be tracked" +
                                                      " between delivery attempts, and other stuff would also be much harder to" +
                                                      " do - therefore, it is a requirement that messages be supplied with an ID.",
                        Headers.MessageId),
                        shortErrorDescription: string.Format("Received message with empty or absent '{0}' header", Headers.MessageId));

                return;
            }

            if (_errorTracker.HasFailedTooManyTimes(messageId))
            {
                // if we don't have 2nd level retries, just get the message out of the way
                if (!_simpleRetryStrategySettings.SecondLevelRetriesEnabled)
                {
                    await
                        MoveMessageToErrorQueue(messageId, transportMessage, transactionContext,
                            GetErrorDescriptionFor(messageId), GetErrorDescriptionFor(messageId, brief: true));

                    _errorTracker.CleanUp(messageId);
                    return;
                }

                // change the identifier to track by to perform this 2nd level of delivery attempts
                var secondLevelMessageId = messageId + "-2nd-level";

                if (_errorTracker.HasFailedTooManyTimes(secondLevelMessageId))
                {
                    await
                        MoveMessageToErrorQueue(messageId, transportMessage, transactionContext,
                            GetErrorDescriptionFor(messageId), GetErrorDescriptionFor(messageId, brief: true));

                    _errorTracker.CleanUp(messageId);
                    _errorTracker.CleanUp(secondLevelMessageId);
                    return;
                }

                context.Save(DispatchAsFailedMessageKey, true);

                await DispatchWithTrackerIdentifier(next, secondLevelMessageId, transactionContext);
                return;
            }

            await DispatchWithTrackerIdentifier(next, messageId, transactionContext);
        }
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transactionContext = context.Load<ITransactionContext>();
            var transportMessage = context.Load<TransportMessage>();

            var clone = transportMessage.Clone();
            clone.Headers[Headers.AuditTime] = RebusTime.Now.ToString("O");

            await _transport.Send(_auditQueue, clone, transactionContext);

            await next();
        }
 public async Task CanRestoreIdentity()
 {
     var step = new RestorePrincipalFromIncomingMessage(new DummySerializer());
     var instance = new Message(new Dictionary<string, string>(), new object());
     var context = new IncomingStepContext(new TransportMessage(new Dictionary<string, string>(), new byte[0] ), new DefaultTransactionContext() );
     instance.Headers[CapturePrincipalInOutgoingMessage.PrincipalCaptureKey] = "Larry";
     context.Save(instance);
     await step.Process(context, async () =>
     {
         Assert.AreEqual(ClaimsPrincipal.Current.Identity.Name, "Larry");
     });
 }
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transportMessage = context.Load<TransportMessage>();
            var headers = transportMessage.Headers;

            if (headers.ContainsKey("rebus-msg-id"))
            {
                MutateLegacyTransportMessage(context, headers, transportMessage);
            }

            await next();
        }
Example #19
0
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var resetEvents = DequeueResetEvents();

            context.Load<ITransactionContext>()
                .OnDisposed(() =>
                {
                    resetEvents.ForEach(resetEvent => resetEvent.Set());
                });

            await next();
        }
Example #20
0
        /// <summary>
        /// For each <see cref="HandlerInvoker"/> found in the current <see cref="IncomingStepContext"/>'s <see cref="HandlerInvokers"/>,
        /// this step will see if the invoker's handler is actually a <see cref="Saga"/>. If that is the case, the saga's correlation properties
        /// are used to see if a piece of existing saga data can be retrieved and mounted on the <see cref="Saga{TSagaData}.Data"/> property.
        /// If no existing instance was found, but the saga implements <see cref="IAmInitiatedBy{TMessage}"/> for the current message,
        /// a new saga data instance will be created (and mounted). Otherwise, the message is ignored.
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            // first we get the relevant handler invokers
            var handlerInvokersForSagas = context.Load<HandlerInvokers>()
                .Where(l => l.HasSaga)
                .ToList();

            // maybe short-circuit? this makes it slightly faster
            if (!handlerInvokersForSagas.Any())
            {
                await next();
                return;
            }

            var message = context.Load<Message>();
            var label = message.GetMessageLabel();

            var body = message.Body;

            // keep track of saga data instances in these two lists
            var loadedSagaData = new List<RelevantSagaInfo>();
            var newlyCreatedSagaData = new List<RelevantSagaInfo>();

            // and then we process them
            foreach (var sagaInvoker in handlerInvokersForSagas)
            {
                await TryMountSagaDataOnInvoker(sagaInvoker, body, label, loadedSagaData, newlyCreatedSagaData);
            }

            // invoke the rest of the pipeline (most likely also dispatching the incoming message to the now-ready saga handlers)
            await next();

            // everything went well - let's divide saga data instances into those to insert, update, and delete
            var newlyCreatedSagaDataToSave = newlyCreatedSagaData.Where(s => !s.Saga.WasMarkedAsComplete && !s.Saga.WasMarkedAsUnchanged);
            var loadedSagaDataToUpdate = loadedSagaData.Where(s => !s.Saga.WasMarkedAsComplete && !s.Saga.WasMarkedAsUnchanged);
            var loadedSagaDataToDelete = loadedSagaData.Where(s => s.Saga.WasMarkedAsComplete);

            foreach (var sagaDataToInsert in newlyCreatedSagaDataToSave)
            {
                await SaveSagaData(sagaDataToInsert, insert: true);
            }

            foreach (var sagaDataToUpdate in loadedSagaDataToUpdate)
            {
                await SaveSagaData(sagaDataToUpdate, insert: false);
            }

            foreach (var sagaDataToUpdate in loadedSagaDataToDelete)
            {
                await _sagaStorage.Delete(sagaDataToUpdate.SagaData);
            }
        }
Example #21
0
        /// <summary>
        /// Invokes the pipeline of <see cref="IIncomingStep"/> steps, passing the given <see cref="IncomingStepContext"/> to each step as it is invoked
        /// </summary>
        public Task Invoke(IncomingStepContext context, IEnumerable <IIncomingStep> pipeline)
        {
            var receivePipeline = GetPipeline(pipeline);
            var step            = TerminationStep;

            for (var index = receivePipeline.Length - 1; index >= 0; index--)
            {
                var nextStep     = step;
                var stepToInvoke = receivePipeline[index];
                step = () => stepToInvoke.Process(context, nextStep);
            }

            return(step());
        }
        /// <summary>
        /// Invokes the pipeline of <see cref="IIncomingStep"/> steps, passing the given <see cref="IncomingStepContext"/> to each step as it is invoked
        /// </summary>
        public async Task Invoke(IncomingStepContext context, IEnumerable <IIncomingStep> pipeline)
        {
            var receivePipeline = pipeline.ToArray();
            var step            = TerminationStep;

            for (var index = receivePipeline.Length - 1; index >= 0; index--)
            {
                var nextStep     = step;
                var stepToInvoke = receivePipeline[index];
                step = () => stepToInvoke.Process(context, nextStep);
            }

            await step();
        }
        /// <summary>
        /// Invokes the pipeline of <see cref="IIncomingStep"/> steps, passing the given <see cref="IncomingStepContext"/> to each step as it is invoked
        /// </summary>
        public async Task Invoke(IncomingStepContext context, IEnumerable<IIncomingStep> pipeline)
        {
            var receivePipeline = pipeline.ToArray();
            var step = TerminationStep;
            
            for (var index = receivePipeline.Length - 1; index >= 0; index--)
            {
                var nextStep = step;
                var stepToInvoke = receivePipeline[index];
                step = () => stepToInvoke.Process(context, nextStep);
            }

            await step();
        }
        public async Task InvokesInOrder()
        {
            var invoker = new DefaultPipelineInvoker();

            var stepContext = new IncomingStepContext(new TransportMessage(new Dictionary<string, string>(), new byte[0]), null);

            await invoker.Invoke(stepContext, new IIncomingStep[]
            {
                new NamedStep("first"),
                new NamedStep("second"),
                new NamedStep("third"),
            });

            Console.WriteLine(string.Join(Environment.NewLine, stepContext.Load<List<string>>()));
        }
Example #25
0
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var begin = RebusTime.Now;

            await next();

            var transactionContext = context.Load<ITransactionContext>();
            var transportMessage = context.Load<TransportMessage>();

            var clone = transportMessage.Clone();
            
            _auditingHelper.SetCommonHeaders(clone);

            clone.Headers[AuditHeaders.HandleTime] = begin.ToString("O");

            await _transport.Send(_auditingHelper.AuditQueue, clone, transactionContext);
        }
        /// <summary>
        /// Descrypts the incoming <see cref="TransportMessage"/> if it has the <see cref="EncryptionHeaders.ContentEncryption"/> header
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transportMessage = context.Load<TransportMessage>();

            if (transportMessage.Headers.ContainsKey(EncryptionHeaders.ContentEncryption))
            {
                var headers = transportMessage.Headers.Clone();
                var encryptedBodyBytes = transportMessage.Body;

                var iv = GetIv(headers);
                var bodyBytes = _encryptor.Decrypt(encryptedBodyBytes, iv);

                context.Save(new TransportMessage(headers, bodyBytes));
            }

            await next();
        }
        /// <summary>
        /// Executes the entire message processing pipeline in an exception handler, tracking the number of failed delivery attempts.
        /// Forwards the message to the error queue when the max number of delivery attempts has been exceeded.
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transportMessage = context.Load<TransportMessage>();
            var transactionContext = context.Load<ITransactionContext>();
            var messageId = transportMessage.Headers.GetValueOrNull(Headers.MessageId);

            if (string.IsNullOrWhiteSpace(messageId))
            {
                await MoveMessageToErrorQueue("<no message ID>", transportMessage,
                    transactionContext, string.Format("Received message with empty or absent '{0}' header! All messages must be" +
                                                      " supplied with an ID . If no ID is present, the message cannot be tracked" +
                                                      " between delivery attempts, and other stuff would also be much harder to" +
                                                      " do - therefore, it is a requirement that messages be supplied with an ID.",
                        Headers.MessageId),
                        shortErrorDescription: "Received message with empty or absent 'rbs2-msg-id' header");

                return;
            }

            if (HasFailedTooManyTimes(messageId))
            {
                await MoveMessageToErrorQueue(messageId, transportMessage, transactionContext, GetErrorDescriptionFor(messageId), GetErrorDescriptionFor(messageId, brief: true));

                RemoveErrorTracking(messageId);

                return;
            }

            try
            {
                await next();
            }
            catch (Exception exception)
            {
                var errorTracking = _trackedErrors.AddOrUpdate(messageId,
                    id => new ErrorTracking(exception),
                    (id, tracking) => tracking.AddError(exception));

                _log.Warn("Unhandled exception {0} while handling message with ID {1}: {2}",
                    errorTracking.Errors.Count(), messageId, exception);

                transactionContext.Abort();
            }
        }
        /// <summary>
        /// Descrypts the incoming <see cref="TransportMessage"/> if it has the <see cref="EncryptionHeaders.ContentEncryption"/> header
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transportMessage = context.Load<TransportMessage>();

            string contentEncryptionValue;
            if (transportMessage.Headers.TryGetValue(EncryptionHeaders.ContentEncryption, out contentEncryptionValue)
                && contentEncryptionValue == _encryptor.ContentEncryptionValue)
            {
                var headers = transportMessage.Headers.Clone();
                var encryptedBodyBytes = transportMessage.Body;

                var iv = GetIv(headers);
                var bodyBytes = _encryptor.Decrypt(new EncryptedData(encryptedBodyBytes, iv));

                context.Save(new TransportMessage(headers, bodyBytes));
            }

            await next();
        }
        public void CheckTiming()
        {
            var invoker = new DefaultPipelineInvoker();

            var stopwatch = Stopwatch.StartNew();

            1000000.Times(() =>
            {
                var stepContext = new IncomingStepContext(new TransportMessage(new Dictionary<string, string>(), new byte[0]), GetFakeTransactionContext());

                var pipeline = Enumerable.Range(0, 15)
                    .Select(stepNumber => new NamedStep($"step {stepNumber}"))
                    .ToArray();

                invoker.Invoke(stepContext, pipeline).Wait();
            });

            Console.WriteLine("Execution took {0:0.0} s", stopwatch.Elapsed.TotalSeconds);
        }
        /// <summary>
        /// Executes the entire message processing pipeline in an exception handler, tracking the number of failed delivery attempts.
        /// Forwards the message to the error queue when the max number of delivery attempts has been exceeded.
        /// </summary>
        public async Task Process(IncomingStepContext context, Func<Task> next)
        {
            var transportMessage = context.Load<TransportMessage>();
            var transactionContext = context.Load<ITransactionContext>();
            var messageId = transportMessage.Headers.GetValueOrNull(Headers.MessageId);

            if (string.IsNullOrWhiteSpace(messageId))
            {
                await MoveMessageToErrorQueue("<no message ID>", transportMessage,
                    transactionContext, string.Format("Received message with empty or absent '{0}' header! All messages must be" +
                                                      " supplied with an ID . If no ID is present, the message cannot be tracked" +
                                                      " between delivery attempts, and other stuff would also be much harder to" +
                                                      " do - therefore, it is a requirement that messages be supplied with an ID.",
                        Headers.MessageId),
                        shortErrorDescription: "Received message with empty or absent 'rbs2-msg-id' header");

                return;
            }

            if (_errorTracker.HasFailedTooManyTimes(messageId))
            {
                await MoveMessageToErrorQueue(messageId, transportMessage, transactionContext, GetErrorDescriptionFor(messageId), GetErrorDescriptionFor(messageId, brief: true));

                _errorTracker.CleanUp(messageId);
                return;
            }

            try
            {
                await next();
            }
            catch (Exception exception)
            {
                _errorTracker.RegisterError(messageId, exception);

                transactionContext.Abort();
            }
        }
            public async Task Process(IncomingStepContext context, Func<Task> next)
            {
                GetActionList(context).Add($"enter {_name}");

                await next();

                GetActionList(context).Add($"leave {_name}");
            }
Example #32
0
 public async Task Process(IncomingStepContext context, Func<Task> next) { }