static void FilterRetainedApplicationMessages(
            IList <MqttApplicationMessage> retainedApplicationMessages,
            CreateSubscriptionResult createSubscriptionResult,
            SubscribeResult subscribeResult)
        {
            for (var index = retainedApplicationMessages.Count - 1; index >= 0; index--)
            {
                var retainedApplicationMessage = retainedApplicationMessages[index];
                if (retainedApplicationMessage == null)
                {
                    continue;
                }

                if (createSubscriptionResult.Subscription.RetainHandling == MqttRetainHandling.DoNotSendOnSubscribe)
                {
                    // This is a MQTT V5+ feature.
                    continue;
                }

                if (createSubscriptionResult.Subscription.RetainHandling == MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly && !createSubscriptionResult.IsNewSubscription)
                {
                    // This is a MQTT V5+ feature.
                    continue;
                }

                if (MqttTopicFilterComparer.Compare(retainedApplicationMessage.Topic, createSubscriptionResult.Subscription.Topic) != MqttTopicFilterCompareResult.IsMatch)
                {
                    continue;
                }

                var retainedMessageMatch = new MqttRetainedMessageMatch
                {
                    ApplicationMessage = retainedApplicationMessage,
                    SubscriptionQualityOfServiceLevel = createSubscriptionResult.Subscription.GrantedQualityOfServiceLevel
                };

                if (subscribeResult.RetainedMessages == null)
                {
                    subscribeResult.RetainedMessages = new List <MqttRetainedMessageMatch>();
                }

                subscribeResult.RetainedMessages.Add(retainedMessageMatch);

                // Clear the retained message from the list because the client should receive every message only
                // one time even if multiple subscriptions affect them.
                retainedApplicationMessages[index] = null;
            }
        }
        public async Task <SubscribeResult> Subscribe(MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
        {
            if (subscribePacket == null)
            {
                throw new ArgumentNullException(nameof(subscribePacket));
            }

            var retainedApplicationMessages = await _retainedMessagesManager.GetMessages().ConfigureAwait(false);

            var result = new SubscribeResult
            {
                ReasonCodes = new List <MqttSubscribeReasonCode>(subscribePacket.TopicFilters.Count)
            };

            var addedSubscriptions = new List <string>();

            // The topic filters are order by its QoS so that the higher QoS will win over a
            // lower one.
            foreach (var originalTopicFilter in subscribePacket.TopicFilters.OrderByDescending(f => f.QualityOfServiceLevel))
            {
                var subscriptionEventArgs = await InterceptSubscribe(originalTopicFilter, cancellationToken).ConfigureAwait(false);

                var finalTopicFilter    = subscriptionEventArgs.TopicFilter;
                var processSubscription = subscriptionEventArgs.ProcessSubscription && subscriptionEventArgs.Response.ReasonCode <= MqttSubscribeReasonCode.GrantedQoS2;

                result.UserProperties = subscriptionEventArgs.UserProperties;
                result.ReasonString   = subscriptionEventArgs.ReasonString;
                result.ReasonCodes.Add(subscriptionEventArgs.Response.ReasonCode);

                if (subscriptionEventArgs.CloseConnection)
                {
                    // When any of the interceptor calls leads to a connection close the connection
                    // must be closed. So do not revert to false!
                    result.CloseConnection = true;
                }

                if (!processSubscription || string.IsNullOrEmpty(finalTopicFilter.Topic))
                {
                    continue;
                }

                var createSubscriptionResult = CreateSubscription(finalTopicFilter, subscribePacket.SubscriptionIdentifier, subscriptionEventArgs.Response.ReasonCode);

                addedSubscriptions.Add(finalTopicFilter.Topic);

                if (_eventContainer.ClientSubscribedTopicEvent.HasHandlers)
                {
                    var eventArgs = new ClientSubscribedTopicEventArgs
                    {
                        ClientId    = _session.Id,
                        TopicFilter = finalTopicFilter
                    };

                    await _eventContainer.ClientSubscribedTopicEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
                }

                FilterRetainedApplicationMessages(retainedApplicationMessages, createSubscriptionResult, result);
            }

            _subscriptionChangedNotification?.OnSubscriptionsAdded(_session, addedSubscriptions);

            return(result);
        }