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.
 
 
 
 

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