You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

146 lines
5.3 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using MQTTnet.Diagnostics;
  7. using MQTTnet.Packets;
  8. namespace MQTTnet.Server
  9. {
  10. public sealed class MqttRetainedMessagesManager
  11. {
  12. private readonly Dictionary<string, MqttApplicationMessage> _retainedMessages = new Dictionary<string, MqttApplicationMessage>();
  13. private readonly SemaphoreSlim _gate = new SemaphoreSlim(1, 1);
  14. private readonly IMqttNetLogger _logger;
  15. private readonly IMqttServerOptions _options;
  16. public MqttRetainedMessagesManager(IMqttServerOptions options, IMqttNetLogger logger)
  17. {
  18. _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  19. _options = options ?? throw new ArgumentNullException(nameof(options));
  20. }
  21. public async Task LoadMessagesAsync()
  22. {
  23. if (_options.Storage == null)
  24. {
  25. return;
  26. }
  27. await _gate.WaitAsync();
  28. try
  29. {
  30. var retainedMessages = await _options.Storage.LoadRetainedMessagesAsync();
  31. _retainedMessages.Clear();
  32. foreach (var retainedMessage in retainedMessages)
  33. {
  34. _retainedMessages[retainedMessage.Topic] = retainedMessage;
  35. }
  36. }
  37. catch (Exception exception)
  38. {
  39. _logger.Error<MqttRetainedMessagesManager>(exception, "Unhandled exception while loading retained messages.");
  40. }
  41. finally
  42. {
  43. _gate.Release();
  44. }
  45. }
  46. public async Task HandleMessageAsync(string clientId, MqttApplicationMessage applicationMessage)
  47. {
  48. if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
  49. await _gate.WaitAsync().ConfigureAwait(false);
  50. try
  51. {
  52. await HandleMessageInternalAsync(clientId, applicationMessage);
  53. }
  54. catch (Exception exception)
  55. {
  56. _logger.Error<MqttRetainedMessagesManager>(exception, "Unhandled exception while handling retained messages.");
  57. }
  58. finally
  59. {
  60. _gate.Release();
  61. }
  62. }
  63. public async Task<List<MqttApplicationMessage>> GetSubscribedMessagesAsync(MqttSubscribePacket subscribePacket)
  64. {
  65. var retainedMessages = new List<MqttApplicationMessage>();
  66. await _gate.WaitAsync().ConfigureAwait(false);
  67. try
  68. {
  69. foreach (var retainedMessage in _retainedMessages.Values)
  70. {
  71. foreach (var topicFilter in subscribePacket.TopicFilters)
  72. {
  73. if (retainedMessage.QualityOfServiceLevel < topicFilter.QualityOfServiceLevel)
  74. {
  75. continue;
  76. }
  77. if (!MqttTopicFilterComparer.IsMatch(retainedMessage.Topic, topicFilter.Topic))
  78. {
  79. continue;
  80. }
  81. retainedMessages.Add(retainedMessage);
  82. break;
  83. }
  84. }
  85. }
  86. finally
  87. {
  88. _gate.Release();
  89. }
  90. return retainedMessages;
  91. }
  92. private async Task HandleMessageInternalAsync(string clientId, MqttApplicationMessage applicationMessage)
  93. {
  94. var saveIsRequired = false;
  95. if (applicationMessage.Payload?.Any() == false)
  96. {
  97. saveIsRequired = _retainedMessages.Remove(applicationMessage.Topic);
  98. _logger.Info<MqttRetainedMessagesManager>("Client '{0}' cleared retained message for topic '{1}'.", clientId, applicationMessage.Topic);
  99. }
  100. else
  101. {
  102. if (!_retainedMessages.ContainsKey(applicationMessage.Topic))
  103. {
  104. _retainedMessages[applicationMessage.Topic] = applicationMessage;
  105. saveIsRequired = true;
  106. }
  107. else
  108. {
  109. var existingMessage = _retainedMessages[applicationMessage.Topic];
  110. if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || !existingMessage.Payload.SequenceEqual(applicationMessage.Payload ?? new byte[0]))
  111. {
  112. _retainedMessages[applicationMessage.Topic] = applicationMessage;
  113. saveIsRequired = true;
  114. }
  115. }
  116. _logger.Info<MqttRetainedMessagesManager>("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic);
  117. }
  118. if (!saveIsRequired)
  119. {
  120. _logger.Trace<MqttRetainedMessagesManager>("Skipped saving retained messages because no changes were detected.");
  121. }
  122. if (saveIsRequired && _options.Storage != null)
  123. {
  124. await _options.Storage.SaveRetainedMessagesAsync(_retainedMessages.Values.ToList());
  125. }
  126. }
  127. }
  128. }