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.
 
 
 
 

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