Extend MQTTnet.Server application.

This commit is contained in:
Christian
2019-03-05 21:37:00 +01:00
parent b68247f56d
commit 6f5f507323
32 changed files with 1114 additions and 309 deletions
@@ -0,0 +1,50 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Protocol;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttApplicationMessageInterceptor : IMqttServerApplicationMessageInterceptor
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger<MqttApplicationMessageInterceptor> _logger;
public MqttApplicationMessageInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttApplicationMessageInterceptor> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task InterceptApplicationMessagePublishAsync(MqttApplicationMessageInterceptorContext context)
{
try
{
var pythonContext = new PythonDictionary
{
{ "accept_publish", context.AcceptPublish },
{ "close_connection", context.CloseConnection },
{ "client_id", context.ClientId },
{ "topic", context.ApplicationMessage.Topic },
{ "qos", (int)context.ApplicationMessage.QualityOfServiceLevel },
{ "retain", context.ApplicationMessage.Retain }
};
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_application_message", pythonContext);
context.AcceptPublish = (bool)pythonContext.get("accept_publish", context.AcceptPublish);
context.CloseConnection = (bool)pythonContext.get("close_connection", context.CloseConnection);
context.ApplicationMessage.Topic = (string)pythonContext.get("topic", context.ApplicationMessage.Topic);
context.ApplicationMessage.QualityOfServiceLevel = (MqttQualityOfServiceLevel)(int)pythonContext.get("qos", (int)context.ApplicationMessage.QualityOfServiceLevel);
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while intercepting application message.");
}
return Task.CompletedTask;
}
}
}
@@ -0,0 +1,46 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Protocol;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttConnectionValidator : IMqttServerConnectionValidator
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger<MqttConnectionValidator> _logger;
public MqttConnectionValidator(PythonScriptHostService pythonScriptHostService, ILogger<MqttConnectionValidator> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task ValidateConnectionAsync(MqttConnectionValidatorContext context)
{
try
{
var pythonContext = new PythonDictionary
{
{ "client_id", context.ClientId },
{ "endpoint", context.Endpoint },
{ "username", context.Username },
{ "password", context.Password },
{ "result", PythonConvert.Pythonfy(context.ReturnCode) }
};
_pythonScriptHostService.InvokeOptionalFunction("on_validate_client_connection", pythonContext);
context.ReturnCode = PythonConvert.ParseEnum<MqttConnectReturnCode>((string)pythonContext["result"]);
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while validating client connection.");
}
return Task.CompletedTask;
}
}
}
@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore;
using MQTTnet.Implementations;
using MQTTnet.Protocol;
using MQTTnet.Server.Logging;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttServerService
{
private readonly ILogger<MqttServerService> _logger;
private readonly MqttConnectionValidator _mqttConnectionValidator;
private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
private readonly MqttApplicationMessageInterceptor _mqttApplicationMessageInterceptor;
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly IMqttServer _mqttServer;
public MqttServerService(
IMqttServerFactory mqttServerFactory,
MqttWebSocketServerAdapter webSocketServerAdapter,
MqttNetLoggerWrapper mqttNetLogger,
MqttConnectionValidator mqttConnectionValidator,
MqttSubscriptionInterceptor mqttSubscriptionInterceptor,
MqttApplicationMessageInterceptor mqttApplicationMessageInterceptor,
PythonScriptHostService pythonScriptHostService,
ILogger<MqttServerService> logger)
{
_mqttConnectionValidator = mqttConnectionValidator ?? throw new ArgumentNullException(nameof(mqttConnectionValidator));
_mqttSubscriptionInterceptor = mqttSubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttSubscriptionInterceptor));
_mqttApplicationMessageInterceptor = mqttApplicationMessageInterceptor ?? throw new ArgumentNullException(nameof(mqttApplicationMessageInterceptor));
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
var adapters = new List<IMqttServerAdapter>
{
new MqttTcpServerAdapter(new MqttNetChildLoggerWrapper(null, mqttNetLogger)),
webSocketServerAdapter
};
_mqttServer = mqttServerFactory.CreateMqttServer(adapters);
}
public void Configure()
{
_pythonScriptHostService.RegisterProxyObject("publish", new Action<PythonDictionary>(Publish));
var options = new MqttServerOptionsBuilder()
.WithDefaultEndpoint()
.WithDefaultEndpointPort(1883)
.WithConnectionValidator(_mqttConnectionValidator)
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
.Build();
_mqttServer.StartAsync(options).GetAwaiter().GetResult();
_logger.LogInformation("MQTT server started.");
}
private void Publish(PythonDictionary parameters)
{
var applicationMessageBuilder = new MqttApplicationMessageBuilder()
.WithTopic((string)parameters.get("topic", null))
.WithRetainFlag((bool)parameters.get("retain", false))
.WithQualityOfServiceLevel((MqttQualityOfServiceLevel)(int)parameters.get("qos", 0));
var payload = parameters.get("payload", null);
var binaryPayload = new byte[0];
if (payload is string stringPayload)
{
binaryPayload = Encoding.UTF8.GetBytes(stringPayload);
}
else if (payload is ByteArray byteArray)
{
binaryPayload = byteArray.ToArray();
}
else if (payload is IEnumerable<int> intArray)
{
binaryPayload = intArray.Select(Convert.ToByte).ToArray();
}
applicationMessageBuilder = applicationMessageBuilder
.WithPayload(binaryPayload);
var applicationMessage = applicationMessageBuilder.Build();
_mqttServer.PublishAsync(applicationMessage).GetAwaiter().GetResult();
_logger.LogInformation($"Published topic '{applicationMessage.Topic}' from server.");
}
}
}
@@ -0,0 +1,47 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttSubscriptionInterceptor : IMqttServerSubscriptionInterceptor
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger<MqttSubscriptionInterceptor> _logger;
public MqttSubscriptionInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttSubscriptionInterceptor> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task InterceptSubscriptionAsync(MqttSubscriptionInterceptorContext context)
{
try
{
var pythonContext = new PythonDictionary
{
{ "accept_subscription", context.AcceptSubscription },
{ "close_connection", context.CloseConnection },
{ "client_id", context.ClientId },
{ "topic", context.TopicFilter.Topic },
{ "qos", (int)context.TopicFilter.QualityOfServiceLevel }
};
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_subscription", pythonContext);
context.AcceptSubscription = (bool)pythonContext["accept_subscription"];
context.CloseConnection = (bool)pythonContext["close_connection"];
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while intercepting subscription.");
}
return Task.CompletedTask;
}
}
}