public CheckSubscriptionsResult CheckSubscriptions(string topic, ulong topicHash, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId) { var possibleSubscriptions = new List <MqttSubscription>(); // Check for possible subscriptions. They might have collisions but this is fine. _subscriptionsLock.Wait(); try { if (_noWildcardSubscriptionsByTopicHash.TryGetValue(topicHash, out var noWildcardSubscriptions)) { possibleSubscriptions.AddRange(noWildcardSubscriptions.ToList()); } foreach (var wcs in _wildcardSubscriptionsByTopicHash) { var wildcardSubscriptions = wcs.Value; var subscriptionHash = wcs.Key; var subscriptionHashMask = wildcardSubscriptions.HashMask; if ((topicHash & subscriptionHashMask) == subscriptionHash) { possibleSubscriptions.AddRange(wildcardSubscriptions.Subscriptions.ToList()); } } } finally { _subscriptionsLock.Release(); } // The pre check has evaluated that nothing is subscribed. // If there were some possible candidates they get checked below // again to avoid collisions. if (possibleSubscriptions.Count == 0) { return(CheckSubscriptionsResult.NotSubscribed); } var senderIsReceiver = string.Equals(senderClientId, _session.Id); var maxQoSLevel = -1; // Not subscribed. HashSet <uint> subscriptionIdentifiers = null; var retainAsPublished = false; foreach (var subscription in possibleSubscriptions) { if (subscription.NoLocal && senderIsReceiver) { // This is a MQTTv5 feature! continue; } if (MqttTopicFilterComparer.Compare(topic, subscription.Topic) != MqttTopicFilterCompareResult.IsMatch) { continue; } if (subscription.RetainAsPublished) { // This is a MQTTv5 feature! retainAsPublished = true; } if ((int)subscription.GrantedQualityOfServiceLevel > maxQoSLevel) { maxQoSLevel = (int)subscription.GrantedQualityOfServiceLevel; } if (subscription.Identifier > 0) { if (subscriptionIdentifiers == null) { subscriptionIdentifiers = new HashSet <uint>(); } subscriptionIdentifiers.Add(subscription.Identifier); } } if (maxQoSLevel == -1) { return(CheckSubscriptionsResult.NotSubscribed); } var result = new CheckSubscriptionsResult { IsSubscribed = true, RetainAsPublished = retainAsPublished, SubscriptionIdentifiers = subscriptionIdentifiers?.ToList() ?? EmptySubscriptionIdentifiers, // Start with the same QoS as the publisher. QualityOfServiceLevel = applicationMessageQoSLevel }; // Now downgrade if required. // // If a subscribing Client has been granted maximum QoS 1 for a particular Topic Filter, then a QoS 0 Application Message matching the filter is delivered // to the Client at QoS 0. This means that at most one copy of the message is received by the Client. On the other hand, a QoS 2 Message published to // the same topic is downgraded by the Server to QoS 1 for delivery to the Client, so that Client might receive duplicate copies of the Message. // Subscribing to a Topic Filter at QoS 2 is equivalent to saying "I would like to receive Messages matching this filter at the QoS with which they were published". // This means a publisher is responsible for determining the maximum QoS a Message can be delivered at, but a subscriber is able to require that the Server // downgrades the QoS to one more suitable for its usage. if (maxQoSLevel < (int)applicationMessageQoSLevel) { result.QualityOfServiceLevel = (MqttQualityOfServiceLevel)maxQoSLevel; } return(result); }