public async Task DispatchApplicationMessage(string senderId, MqttApplicationMessage applicationMessage) { try { if (applicationMessage.Retain) { await _retainedMessagesManager.UpdateMessage(senderId, applicationMessage).ConfigureAwait(false); } var deliveryCount = 0; List <MqttSession> subscriberSessions; lock (_sessionsManagementLock) { // only subscriber clients are of interest here. subscriberSessions = _subscriberSessions.ToList(); } // Calculate application message topic hash once for subscription checks MqttSubscription.CalculateTopicHash(applicationMessage.Topic, out var topicHash, out _, out _); foreach (var session in subscriberSessions) { var checkSubscriptionsResult = session.SubscriptionsManager.CheckSubscriptions( applicationMessage.Topic, topicHash, applicationMessage.QualityOfServiceLevel, senderId); if (!checkSubscriptionsResult.IsSubscribed) { continue; } var newPublishPacket = _packetFactories.Publish.Create(applicationMessage); newPublishPacket.QualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel; newPublishPacket.SubscriptionIdentifiers = checkSubscriptionsResult.SubscriptionIdentifiers; if (newPublishPacket.QualityOfServiceLevel > 0) { newPublishPacket.PacketIdentifier = session.PacketIdentifierProvider.GetNextPacketIdentifier(); } if (checkSubscriptionsResult.RetainAsPublished) { // Transfer the original retain state from the publisher. This is a MQTTv5 feature. newPublishPacket.Retain = applicationMessage.Retain; } else { newPublishPacket.Retain = false; } session.EnqueueDataPacket(new MqttPacketBusItem(newPublishPacket)); deliveryCount++; _logger.Verbose("Client '{0}': Queued PUBLISH packet with topic '{1}'.", session.Id, applicationMessage.Topic); } await FireApplicationMessageNotConsumedEvent(applicationMessage, deliveryCount, senderId); } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while processing next queued application message."); } }
CreateSubscriptionResult CreateSubscription(MqttTopicFilter topicFilter, uint subscriptionIdentifier, MqttSubscribeReasonCode reasonCode) { MqttQualityOfServiceLevel grantedQualityOfServiceLevel; if (reasonCode == MqttSubscribeReasonCode.GrantedQoS0) { grantedQualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; } else if (reasonCode == MqttSubscribeReasonCode.GrantedQoS1) { grantedQualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce; } else if (reasonCode == MqttSubscribeReasonCode.GrantedQoS2) { grantedQualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce; } else { throw new InvalidOperationException(); } var subscription = new MqttSubscription( topicFilter.Topic, topicFilter.NoLocal, topicFilter.RetainHandling, topicFilter.RetainAsPublished, grantedQualityOfServiceLevel, subscriptionIdentifier); bool isNewSubscription; // Add to subscriptions and maintain topic hash dictionaries _subscriptionsLock.Wait(); try { MqttSubscription.CalculateTopicHash(topicFilter.Topic, out var topicHash, out var topicHashMask, out var hasWildcard); if (_subscriptions.TryGetValue(topicFilter.Topic, out var existingSubscription)) { // must remove object from topic hash dictionary first if (hasWildcard) { if (_wildcardSubscriptionsByTopicHash.TryGetValue(topicHash, out var subs)) { subs.Subscriptions.Remove(existingSubscription); // no need to remove empty entry because we'll be adding subscription again below } } else { if (_noWildcardSubscriptionsByTopicHash.TryGetValue(topicHash, out var subscriptions)) { subscriptions.Remove(existingSubscription); // no need to remove empty entry because we'll be adding subscription again below } } } isNewSubscription = existingSubscription == null; _subscriptions[topicFilter.Topic] = subscription; // Add or re-add to topic hash dictionary if (hasWildcard) { if (!_wildcardSubscriptionsByTopicHash.TryGetValue(topicHash, out var subscriptions)) { subscriptions = new TopicHashMaskSubscriptions(topicHashMask); _wildcardSubscriptionsByTopicHash.Add(topicHash, subscriptions); } subscriptions.Subscriptions.Add(subscription); } else { if (!_noWildcardSubscriptionsByTopicHash.TryGetValue(topicHash, out var subscriptions)) { subscriptions = new HashSet <MqttSubscription>(); _noWildcardSubscriptionsByTopicHash.Add(topicHash, subscriptions); } subscriptions.Add(subscription); } } finally { _subscriptionsLock.Release(); } return(new CreateSubscriptionResult { IsNewSubscription = isNewSubscription, Subscription = subscription }); }