mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-27 08:42:37 +08:00
Add interceptor for unsubscriptions.
This commit is contained in:
@@ -33,6 +33,7 @@ namespace MQTTnet.Server.Mqtt
|
|||||||
private readonly MqttServerConnectionValidator _mqttConnectionValidator;
|
private readonly MqttServerConnectionValidator _mqttConnectionValidator;
|
||||||
private readonly IMqttServer _mqttServer;
|
private readonly IMqttServer _mqttServer;
|
||||||
private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
|
private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
|
||||||
|
private readonly MqttUnsubscriptionInterceptor _mqttUnsubscriptionInterceptor;
|
||||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||||
private readonly MqttWebSocketServerAdapter _webSocketServerAdapter;
|
private readonly MqttWebSocketServerAdapter _webSocketServerAdapter;
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ namespace MQTTnet.Server.Mqtt
|
|||||||
MqttClientUnsubscribedTopicHandler mqttClientUnsubscribedTopicHandler,
|
MqttClientUnsubscribedTopicHandler mqttClientUnsubscribedTopicHandler,
|
||||||
MqttServerConnectionValidator mqttConnectionValidator,
|
MqttServerConnectionValidator mqttConnectionValidator,
|
||||||
MqttSubscriptionInterceptor mqttSubscriptionInterceptor,
|
MqttSubscriptionInterceptor mqttSubscriptionInterceptor,
|
||||||
|
MqttUnsubscriptionInterceptor mqttUnsubscriptionInterceptor,
|
||||||
MqttApplicationMessageInterceptor mqttApplicationMessageInterceptor,
|
MqttApplicationMessageInterceptor mqttApplicationMessageInterceptor,
|
||||||
MqttServerStorage mqttServerStorage,
|
MqttServerStorage mqttServerStorage,
|
||||||
PythonScriptHostService pythonScriptHostService,
|
PythonScriptHostService pythonScriptHostService,
|
||||||
@@ -57,6 +59,7 @@ namespace MQTTnet.Server.Mqtt
|
|||||||
_mqttClientUnsubscribedTopicHandler = mqttClientUnsubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientUnsubscribedTopicHandler));
|
_mqttClientUnsubscribedTopicHandler = mqttClientUnsubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientUnsubscribedTopicHandler));
|
||||||
_mqttConnectionValidator = mqttConnectionValidator ?? throw new ArgumentNullException(nameof(mqttConnectionValidator));
|
_mqttConnectionValidator = mqttConnectionValidator ?? throw new ArgumentNullException(nameof(mqttConnectionValidator));
|
||||||
_mqttSubscriptionInterceptor = mqttSubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttSubscriptionInterceptor));
|
_mqttSubscriptionInterceptor = mqttSubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttSubscriptionInterceptor));
|
||||||
|
_mqttUnsubscriptionInterceptor = mqttUnsubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttUnsubscriptionInterceptor));
|
||||||
_mqttApplicationMessageInterceptor = mqttApplicationMessageInterceptor ?? throw new ArgumentNullException(nameof(mqttApplicationMessageInterceptor));
|
_mqttApplicationMessageInterceptor = mqttApplicationMessageInterceptor ?? throw new ArgumentNullException(nameof(mqttApplicationMessageInterceptor));
|
||||||
_mqttServerStorage = mqttServerStorage ?? throw new ArgumentNullException(nameof(mqttServerStorage));
|
_mqttServerStorage = mqttServerStorage ?? throw new ArgumentNullException(nameof(mqttServerStorage));
|
||||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||||
@@ -178,6 +181,7 @@ namespace MQTTnet.Server.Mqtt
|
|||||||
.WithConnectionValidator(_mqttConnectionValidator)
|
.WithConnectionValidator(_mqttConnectionValidator)
|
||||||
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
|
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
|
||||||
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
|
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
|
||||||
|
.WithUnsubscriptionInterceptor(_mqttUnsubscriptionInterceptor)
|
||||||
.WithStorage(_mqttServerStorage);
|
.WithStorage(_mqttServerStorage);
|
||||||
|
|
||||||
// Configure unencrypted connections
|
// Configure unencrypted connections
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using IronPython.Runtime;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MQTTnet.Server.Scripting;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server.Mqtt
|
||||||
|
{
|
||||||
|
public class MqttUnsubscriptionInterceptor : IMqttServerUnsubscriptionInterceptor
|
||||||
|
{
|
||||||
|
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public MqttUnsubscriptionInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttUnsubscriptionInterceptor> logger)
|
||||||
|
{
|
||||||
|
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InterceptUnsubscriptionAsync(MqttUnsubscriptionInterceptorContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sessionItems = (PythonDictionary)context.SessionItems[MqttServerConnectionValidator.WrappedSessionItemsKey];
|
||||||
|
|
||||||
|
var pythonContext = new PythonDictionary
|
||||||
|
{
|
||||||
|
{ "client_id", context.ClientId },
|
||||||
|
{ "session_items", sessionItems },
|
||||||
|
{ "topic", context.Topic },
|
||||||
|
{ "accept_unsubscription", context.AcceptUnsubscription },
|
||||||
|
{ "close_connection", context.CloseConnection }
|
||||||
|
};
|
||||||
|
|
||||||
|
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_unsubscription", pythonContext);
|
||||||
|
|
||||||
|
context.AcceptUnsubscription = (bool)pythonContext["accept_unsubscription"];
|
||||||
|
context.CloseConnection = (bool)pythonContext["close_connection"];
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(exception, "Error while intercepting unsubscription.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
IMqttServerConnectionValidator ConnectionValidator { get; }
|
IMqttServerConnectionValidator ConnectionValidator { get; }
|
||||||
IMqttServerSubscriptionInterceptor SubscriptionInterceptor { get; }
|
IMqttServerSubscriptionInterceptor SubscriptionInterceptor { get; }
|
||||||
|
IMqttServerUnsubscriptionInterceptor UnsubscriptionInterceptor { get; }
|
||||||
IMqttServerApplicationMessageInterceptor ApplicationMessageInterceptor { get; }
|
IMqttServerApplicationMessageInterceptor ApplicationMessageInterceptor { get; }
|
||||||
IMqttServerClientMessageQueueInterceptor ClientMessageQueueInterceptor { get; }
|
IMqttServerClientMessageQueueInterceptor ClientMessageQueueInterceptor { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server
|
||||||
|
{
|
||||||
|
public interface IMqttServerUnsubscriptionInterceptor
|
||||||
|
{
|
||||||
|
Task InterceptUnsubscriptionAsync(MqttUnsubscriptionInterceptorContext context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -107,9 +107,16 @@ namespace MQTTnet.Server
|
|||||||
PacketIdentifier = unsubscribePacket.PacketIdentifier
|
PacketIdentifier = unsubscribePacket.PacketIdentifier
|
||||||
};
|
};
|
||||||
|
|
||||||
lock (_subscriptions)
|
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
||||||
{
|
{
|
||||||
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
var interceptorContext = await InterceptUnsubscribeAsync(topicFilter).ConfigureAwait(false);
|
||||||
|
if (!interceptorContext.AcceptUnsubscription)
|
||||||
|
{
|
||||||
|
unsubAckPacket.ReasonCodes.Add(MqttUnsubscribeReasonCode.ImplementationSpecificError);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_subscriptions)
|
||||||
{
|
{
|
||||||
if (_subscriptions.Remove(topicFilter))
|
if (_subscriptions.Remove(topicFilter))
|
||||||
{
|
{
|
||||||
@@ -130,19 +137,23 @@ namespace MQTTnet.Server
|
|||||||
return unsubAckPacket;
|
return unsubAckPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task UnsubscribeAsync(IEnumerable<string> topicFilters)
|
public async Task UnsubscribeAsync(IEnumerable<string> topicFilters)
|
||||||
{
|
{
|
||||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||||
|
|
||||||
lock (_subscriptions)
|
foreach (var topicFilter in topicFilters)
|
||||||
{
|
{
|
||||||
foreach (var topicFilter in topicFilters)
|
var interceptorContext = await InterceptUnsubscribeAsync(topicFilter).ConfigureAwait(false);
|
||||||
|
if (!interceptorContext.AcceptUnsubscription)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_subscriptions)
|
||||||
{
|
{
|
||||||
_subscriptions.Remove(topicFilter);
|
_subscriptions.Remove(topicFilter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel qosLevel)
|
public CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel qosLevel)
|
||||||
@@ -206,6 +217,17 @@ namespace MQTTnet.Server
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<MqttUnsubscriptionInterceptorContext> InterceptUnsubscribeAsync(string topicFilter)
|
||||||
|
{
|
||||||
|
var context = new MqttUnsubscriptionInterceptorContext(_clientSession.ClientId, topicFilter, _clientSession.Items);
|
||||||
|
if (_serverOptions.UnsubscriptionInterceptor != null)
|
||||||
|
{
|
||||||
|
await _serverOptions.UnsubscriptionInterceptor.InterceptUnsubscriptionAsync(context).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
private static CheckSubscriptionsResult CreateSubscriptionResult(MqttQualityOfServiceLevel qosLevel, HashSet<MqttQualityOfServiceLevel> subscribedQoSLevels)
|
private static CheckSubscriptionsResult CreateSubscriptionResult(MqttQualityOfServiceLevel qosLevel, HashSet<MqttQualityOfServiceLevel> subscribedQoSLevels)
|
||||||
{
|
{
|
||||||
MqttQualityOfServiceLevel effectiveQoS;
|
MqttQualityOfServiceLevel effectiveQoS;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public IMqttServerSubscriptionInterceptor SubscriptionInterceptor { get; set; }
|
public IMqttServerSubscriptionInterceptor SubscriptionInterceptor { get; set; }
|
||||||
|
|
||||||
|
public IMqttServerUnsubscriptionInterceptor UnsubscriptionInterceptor { get; set; }
|
||||||
|
|
||||||
public IMqttServerStorage Storage { get; set; }
|
public IMqttServerStorage Storage { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,6 +155,12 @@ namespace MQTTnet.Server
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttServerOptionsBuilder WithUnsubscriptionInterceptor(IMqttServerUnsubscriptionInterceptor value)
|
||||||
|
{
|
||||||
|
_options.UnsubscriptionInterceptor = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttServerOptionsBuilder WithSubscriptionInterceptor(Action<MqttSubscriptionInterceptorContext> value)
|
public MqttServerOptionsBuilder WithSubscriptionInterceptor(Action<MqttSubscriptionInterceptorContext> value)
|
||||||
{
|
{
|
||||||
_options.SubscriptionInterceptor = new MqttServerSubscriptionInterceptorDelegate(value);
|
_options.SubscriptionInterceptor = new MqttServerSubscriptionInterceptorDelegate(value);
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server
|
||||||
|
{
|
||||||
|
public class MqttUnsubscriptionInterceptorContext
|
||||||
|
{
|
||||||
|
public MqttUnsubscriptionInterceptorContext(string clientId, string topic, IDictionary<object, object> sessionItems)
|
||||||
|
{
|
||||||
|
ClientId = clientId;
|
||||||
|
Topic = topic;
|
||||||
|
SessionItems = sessionItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ClientId { get; }
|
||||||
|
|
||||||
|
public string Topic { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a key/value collection that can be used to share data within the scope of this session.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<object, object> SessionItems { get; }
|
||||||
|
|
||||||
|
public bool AcceptUnsubscription { get; set; } = true;
|
||||||
|
|
||||||
|
public bool CloseConnection { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user