No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 

165 líneas
6.0 KiB

  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using MQTTnet.Packets;
  8. using MQTTnet.Protocol;
  9. namespace MQTTnet.Server
  10. {
  11. public sealed class MqttClientSubscriptionsManager : IDisposable
  12. {
  13. private readonly ConcurrentDictionary<string, MqttQualityOfServiceLevel> _subscriptions = new ConcurrentDictionary<string, MqttQualityOfServiceLevel>();
  14. private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
  15. private readonly IMqttServerOptions _options;
  16. private readonly MqttServer _server;
  17. private readonly string _clientId;
  18. public MqttClientSubscriptionsManager(string clientId, IMqttServerOptions options, MqttServer server)
  19. {
  20. _clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
  21. _options = options ?? throw new ArgumentNullException(nameof(options));
  22. _server = server;
  23. }
  24. public MqttClientSubscribeResult Subscribe(MqttSubscribePacket subscribePacket)
  25. {
  26. if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
  27. var result = new MqttClientSubscribeResult
  28. {
  29. ResponsePacket = new MqttSubAckPacket
  30. {
  31. PacketIdentifier = subscribePacket.PacketIdentifier
  32. },
  33. CloseConnection = false
  34. };
  35. foreach (var topicFilter in subscribePacket.TopicFilters)
  36. {
  37. var interceptorContext = InterceptSubscribe(topicFilter);
  38. if (!interceptorContext.AcceptSubscription)
  39. {
  40. result.ResponsePacket.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.Failure);
  41. }
  42. else
  43. {
  44. result.ResponsePacket.SubscribeReturnCodes.Add(ConvertToMaximumQoS(topicFilter.QualityOfServiceLevel));
  45. }
  46. if (interceptorContext.CloseConnection)
  47. {
  48. result.CloseConnection = true;
  49. }
  50. if (interceptorContext.AcceptSubscription)
  51. {
  52. _subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
  53. _server.OnClientSubscribedTopic(_clientId, topicFilter);
  54. }
  55. }
  56. return result;
  57. }
  58. public MqttUnsubAckPacket Unsubscribe(MqttUnsubscribePacket unsubscribePacket)
  59. {
  60. if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
  61. foreach (var topicFilter in unsubscribePacket.TopicFilters)
  62. {
  63. _subscriptions.TryRemove(topicFilter, out _);
  64. _server.OnClientUnsubscribedTopic(_clientId, topicFilter);
  65. }
  66. return new MqttUnsubAckPacket
  67. {
  68. PacketIdentifier = unsubscribePacket.PacketIdentifier
  69. };
  70. }
  71. public async Task<CheckSubscriptionsResult> CheckSubscriptionsAsync(MqttApplicationMessage applicationMessage)
  72. {
  73. if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
  74. await _semaphore.WaitAsync().ConfigureAwait(false);
  75. try
  76. {
  77. var qosLevels = new HashSet<MqttQualityOfServiceLevel>();
  78. foreach (var subscription in _subscriptions)
  79. {
  80. if (!MqttTopicFilterComparer.IsMatch(applicationMessage.Topic, subscription.Key))
  81. {
  82. continue;
  83. }
  84. qosLevels.Add(subscription.Value);
  85. }
  86. if (qosLevels.Count == 0)
  87. {
  88. return new CheckSubscriptionsResult
  89. {
  90. IsSubscribed = false
  91. };
  92. }
  93. return CreateSubscriptionResult(applicationMessage, qosLevels);
  94. }
  95. finally
  96. {
  97. _semaphore.Release();
  98. }
  99. }
  100. public void Dispose()
  101. {
  102. _semaphore?.Dispose();
  103. }
  104. private static MqttSubscribeReturnCode ConvertToMaximumQoS(MqttQualityOfServiceLevel qualityOfServiceLevel)
  105. {
  106. switch (qualityOfServiceLevel)
  107. {
  108. case MqttQualityOfServiceLevel.AtMostOnce: return MqttSubscribeReturnCode.SuccessMaximumQoS0;
  109. case MqttQualityOfServiceLevel.AtLeastOnce: return MqttSubscribeReturnCode.SuccessMaximumQoS1;
  110. case MqttQualityOfServiceLevel.ExactlyOnce: return MqttSubscribeReturnCode.SuccessMaximumQoS2;
  111. default: return MqttSubscribeReturnCode.Failure;
  112. }
  113. }
  114. private MqttSubscriptionInterceptorContext InterceptSubscribe(TopicFilter topicFilter)
  115. {
  116. var interceptorContext = new MqttSubscriptionInterceptorContext(_clientId, topicFilter);
  117. _options.SubscriptionInterceptor?.Invoke(interceptorContext);
  118. return interceptorContext;
  119. }
  120. private static CheckSubscriptionsResult CreateSubscriptionResult(MqttApplicationMessage applicationMessage, HashSet<MqttQualityOfServiceLevel> subscribedQoSLevels)
  121. {
  122. MqttQualityOfServiceLevel effectiveQoS;
  123. if (subscribedQoSLevels.Contains(applicationMessage.QualityOfServiceLevel))
  124. {
  125. effectiveQoS = applicationMessage.QualityOfServiceLevel;
  126. }
  127. else if (subscribedQoSLevels.Count == 1)
  128. {
  129. effectiveQoS = subscribedQoSLevels.First();
  130. }
  131. else
  132. {
  133. effectiveQoS = subscribedQoSLevels.Max();
  134. }
  135. return new CheckSubscriptionsResult
  136. {
  137. IsSubscribed = true,
  138. QualityOfServiceLevel = effectiveQoS
  139. };
  140. }
  141. }
  142. }