async Task <MqttPublishPacket> CreatePublishPacket(MqttQueuedApplicationMessage queuedApplicationMessage) { var publishPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreatePublishPacket(queuedApplicationMessage.ApplicationMessage); publishPacket.QualityOfServiceLevel = queuedApplicationMessage.SubscriptionQualityOfServiceLevel; if (_channelAdapter.PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.V500) { publishPacket.Properties.SubscriptionIdentifiers = queuedApplicationMessage.SubscriptionIdentifiers; } // Set the retain flag to true according to [MQTT-3.3.1-8] and [MQTT-3.3.1-9]. publishPacket.Retain = queuedApplicationMessage.IsRetainedMessage; publishPacket = await InvokeClientMessageQueueInterceptor(publishPacket, queuedApplicationMessage).ConfigureAwait(false); if (publishPacket == null) { // The interceptor has decided that the message is not relevant and will be fully ignored. return(null); } if (publishPacket.QualityOfServiceLevel > 0) { publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier(); } return(publishPacket); }
public void Enqueue(MqttQueuedApplicationMessage queuedApplicationMessage) { if (queuedApplicationMessage == null) { throw new ArgumentNullException(nameof(queuedApplicationMessage)); } lock (_messageQueue) { if (_messageQueue.Count >= _options.MaxPendingMessagesPerClient) { if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage) { return; } if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage) { _messageQueue.TryDequeue(); } } _messageQueue.Enqueue(queuedApplicationMessage); } }
static void FilterRetainedApplicationMessages(IList <MqttApplicationMessage> retainedApplicationMessages, Subscription subscription, SubscribeResult subscribeResult) { for (var i = retainedApplicationMessages.Count - 1; i >= 0; i--) { var retainedApplicationMessage = retainedApplicationMessages[i]; if (retainedApplicationMessage == null) { continue; } if (subscription.RetainHandling == MqttRetainHandling.DoNotSendOnSubscribe) { // This is a MQTT V5+ feature. continue; } if (subscription.RetainHandling == MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly && !subscription.IsNewSubscription) { // This is a MQTT V5+ feature. continue; } if (!MqttTopicFilterComparer.IsMatch(retainedApplicationMessage.Topic, subscription.Topic)) { continue; } var queuedApplicationMessage = new MqttQueuedApplicationMessage { ApplicationMessage = retainedApplicationMessage, IsRetainedMessage = true, SubscriptionQualityOfServiceLevel = subscription.QualityOfServiceLevel }; if (subscription.Identifier > 0) { queuedApplicationMessage.SubscriptionIdentifiers = new List <uint> { subscription.Identifier }; } subscribeResult.RetainedApplicationMessages.Add(queuedApplicationMessage); retainedApplicationMessages[i] = null; } }
async Task TryProcessNextQueuedApplicationMessage() { try { MqttPendingApplicationMessage pendingApplicationMessage; try { pendingApplicationMessage = _messageQueue.Take(); } catch (ArgumentNullException) { return; } catch (ObjectDisposedException) { return; } var clientConnection = pendingApplicationMessage.Sender; var senderClientId = clientConnection?.ClientId ?? _options.ClientId; var applicationMessage = pendingApplicationMessage.ApplicationMessage; var interceptor = _options.ApplicationMessageInterceptor; if (interceptor != null) { var interceptorContext = await InterceptApplicationMessageAsync(interceptor, clientConnection, applicationMessage) .ConfigureAwait(false); if (interceptorContext != null) { if (interceptorContext.CloseConnection) { if (clientConnection != null) { await clientConnection.StopAsync(MqttClientDisconnectReason.NormalDisconnection) .ConfigureAwait(false); } } if (interceptorContext.ApplicationMessage == null || !interceptorContext.AcceptPublish) { return; } applicationMessage = interceptorContext.ApplicationMessage; } } await _eventDispatcher.SafeNotifyApplicationMessageReceivedAsync(senderClientId, applicationMessage) .ConfigureAwait(false); if (applicationMessage.Retain) { await _retainedMessagesManager.HandleMessageAsync(senderClientId, applicationMessage) .ConfigureAwait(false); } var deliveryCount = 0; List <MqttClientSession> sessions; lock (_clientSessions) { sessions = _clientSessions.Values.ToList(); } foreach (var clientSession in sessions) { var checkSubscriptionsResult = clientSession.SubscriptionsManager.CheckSubscriptions( applicationMessage.Topic, applicationMessage.QualityOfServiceLevel, senderClientId); if (!checkSubscriptionsResult.IsSubscribed) { continue; } _logger.Verbose("Client '{0}': Queued application message with topic '{1}'.", clientSession.ClientId, applicationMessage.Topic); var queuedApplicationMessage = new MqttQueuedApplicationMessage { ApplicationMessage = applicationMessage, SubscriptionQualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel, SubscriptionIdentifiers = checkSubscriptionsResult.SubscriptionIdentifiers }; if (checkSubscriptionsResult.RetainAsPublished) { // Transfer the original retain state from the publisher. // This is a MQTTv5 feature. queuedApplicationMessage.IsRetainedMessage = applicationMessage.Retain; } clientSession.ApplicationMessagesQueue.Enqueue(queuedApplicationMessage); deliveryCount++; } if (deliveryCount == 0) { var undeliveredMessageInterceptor = _options.UndeliveredMessageInterceptor; if (undeliveredMessageInterceptor == null) { return; } // The delegate signature is the same as for regular message interceptor. So the call is fine and just uses a different interceptor. await InterceptApplicationMessageAsync(undeliveredMessageInterceptor, clientConnection, applicationMessage).ConfigureAwait(false); } } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while processing next queued application message."); } }
async Task <MqttPublishPacket> InvokeClientMessageQueueInterceptor(MqttPublishPacket publishPacket, MqttQueuedApplicationMessage queuedApplicationMessage) { if (_serverOptions.ClientMessageQueueInterceptor == null) { return(publishPacket); } var context = new MqttClientMessageQueueInterceptorContext { SenderClientId = queuedApplicationMessage.SenderClientId, ReceiverClientId = ClientId, ApplicationMessage = queuedApplicationMessage.ApplicationMessage, SubscriptionQualityOfServiceLevel = queuedApplicationMessage.SubscriptionQualityOfServiceLevel }; if (_serverOptions.ClientMessageQueueInterceptor != null) { await _serverOptions.ClientMessageQueueInterceptor.InterceptClientMessageQueueEnqueueAsync(context).ConfigureAwait(false); } if (!context.AcceptEnqueue || context.ApplicationMessage == null) { return(null); } publishPacket.Topic = context.ApplicationMessage.Topic; publishPacket.Payload = context.ApplicationMessage.Payload; publishPacket.QualityOfServiceLevel = context.SubscriptionQualityOfServiceLevel; return(publishPacket); }
async Task SendPacketsLoop(CancellationToken cancellationToken) { MqttQueuedApplicationMessage queuedApplicationMessage = null; MqttPublishPacket publishPacket = null; try { while (!cancellationToken.IsCancellationRequested) { queuedApplicationMessage = await Session.ApplicationMessagesQueue.Dequeue(cancellationToken).ConfigureAwait(false); // Also check the cancellation token here because the dequeue is blocking and may take some time. if (cancellationToken.IsCancellationRequested) { return; } if (queuedApplicationMessage == null) { continue; } publishPacket = await CreatePublishPacket(queuedApplicationMessage).ConfigureAwait(false); if (publishPacket == null) { continue; } if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) { await SendPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false); } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) { using (var awaitable = _packetDispatcher.AddAwaitable <MqttPubAckPacket>(publishPacket.PacketIdentifier)) { await SendPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false); await awaitable.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); } } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) { using (var awaitableRec = _packetDispatcher.AddAwaitable <MqttPubRecPacket>(publishPacket.PacketIdentifier)) using (var awaitableComp = _packetDispatcher.AddAwaitable <MqttPubCompPacket>(publishPacket.PacketIdentifier)) { await SendPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false); var pubRecPacket = await awaitableRec.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); var pubRelPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success); await SendPacketAsync(pubRelPacket, cancellationToken).ConfigureAwait(false); await awaitableComp.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); } } _logger.Verbose("Client '{0}': Queued application message sent.", ClientId); } } catch (OperationCanceledException) { } catch (Exception exception) { if (exception is MqttCommunicationTimedOutException) { _logger.Warning(exception, "Client '{0}': Sending publish packet failed: Timeout.", ClientId); } else if (exception is MqttCommunicationException) { _logger.Warning(exception, "Client '{0}': Sending publish packet failed: Communication exception.", ClientId); } else { _logger.Error(exception, "Client '{0}': Sending publish packet failed.", ClientId); } if (publishPacket?.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) { if (queuedApplicationMessage != null) { queuedApplicationMessage.IsDuplicate = true; Session.ApplicationMessagesQueue.Enqueue(queuedApplicationMessage); } } StopInternal(); } }