Extend configuration.

This commit is contained in:
Christian Kratky
2019-03-11 18:43:39 +01:00
parent cd76f434e3
commit e1799ea6a3
8 changed files with 228 additions and 120 deletions
@@ -27,12 +27,17 @@
/// <summary> /// <summary>
/// Listen Settings /// Listen Settings
/// </summary> /// </summary>
public ListenModel EndPoint { get; set; } = new ListenModel(); public TcpEndpointModel TcpEndPoint { get; set; } = new TcpEndpointModel();
/// <summary> /// <summary>
/// Encryption Listen Settings /// Encryption Listen Settings
/// </summary> /// </summary>
public ListenModel EndPointEncryption { get; set; } = new ListenModel(); public TcpEndpointModel EncryptedTcpEndPoint { get; set; } = new TcpEndpointModel();
/// <summary>
/// Settings for the Web Socket endpoint.
/// </summary>
public WebSocketEndpointModel WebSocketEndpoint { get; set; } = new WebSocketEndpointModel();
/// <summary> /// <summary>
/// Set limit for max pending messages per client /// Set limit for max pending messages per client
@@ -6,12 +6,12 @@ namespace MQTTnet.Server.Configuration
/// <summary> /// <summary>
/// Listen Entry Settings Model /// Listen Entry Settings Model
/// </summary> /// </summary>
public class ListenModel public class TcpEndpointModel
{ {
/// <summary> /// <summary>
/// Constructor /// Constructor
/// </summary> /// </summary>
public ListenModel() public TcpEndpointModel()
{ {
} }
@@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace MQTTnet.Server.Configuration
{
public class WebSocketEndpointModel
{
public bool Enabled { get; set; } = true;
public string Path { get; set; } = "/mqtt";
public int ReceiveBufferSize { get; set; } = 4096;
public int KeepAliveInterval { get; set; } = 120;
public List<string> AllowedOrigins { get; set; }
}
}
+82 -69
View File
@@ -1,8 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.WebSockets;
using System.Text; using System.Text;
using System.Threading.Tasks;
using IronPython.Runtime; using IronPython.Runtime;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MQTTnet.Adapter; using MQTTnet.Adapter;
using MQTTnet.AspNetCore; using MQTTnet.AspNetCore;
@@ -26,6 +29,7 @@ namespace MQTTnet.Server.Mqtt
private readonly IMqttServer _mqttServer; private readonly IMqttServer _mqttServer;
private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor; private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
private readonly PythonScriptHostService _pythonScriptHostService; private readonly PythonScriptHostService _pythonScriptHostService;
private readonly MqttWebSocketServerAdapter _webSocketServerAdapter;
public MqttServerService( public MqttServerService(
Configuration.SettingsModel settings, Configuration.SettingsModel settings,
@@ -51,97 +55,36 @@ namespace MQTTnet.Server.Mqtt
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService)); _pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
WebSocketServerAdapter = new MqttWebSocketServerAdapter(mqttFactory.Logger.CreateChildLogger()); _webSocketServerAdapter = new MqttWebSocketServerAdapter(mqttFactory.Logger.CreateChildLogger());
var adapters = new List<IMqttServerAdapter> var adapters = new List<IMqttServerAdapter>
{ {
new MqttTcpServerAdapter(mqttFactory.Logger.CreateChildLogger()), new MqttTcpServerAdapter(mqttFactory.Logger.CreateChildLogger()),
WebSocketServerAdapter _webSocketServerAdapter
}; };
_mqttServer = mqttFactory.CreateMqttServer(adapters); _mqttServer = mqttFactory.CreateMqttServer(adapters);
} }
public MqttWebSocketServerAdapter WebSocketServerAdapter { get; }
public void Configure() public void Configure()
{ {
_pythonScriptHostService.RegisterProxyObject("publish", new Action<PythonDictionary>(Publish)); _pythonScriptHostService.RegisterProxyObject("publish", new Action<PythonDictionary>(Publish));
var options = new MqttServerOptionsBuilder()
.WithMaxPendingMessagesPerClient(_settings.MaxPendingMessagesPerClient)
.WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(_settings.CommunicationTimeout))
.WithConnectionValidator(_mqttConnectionValidator)
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor);
// Configure unencrypted connections
if (_settings.EndPoint.Enabled)
{
options.WithDefaultEndpoint();
if (_settings.EndPoint.TryReadIPv4(out var address4))
{
options.WithDefaultEndpointBoundIPAddress(address4);
}
if (_settings.EndPoint.TryReadIPv6(out var address6))
{
options.WithDefaultEndpointBoundIPV6Address(address6);
}
if (_settings.EndPoint.Port > 0)
{
options.WithDefaultEndpointPort(_settings.EndPoint.Port);
}
}
else
{
options.WithoutDefaultEndpoint();
}
// Configure encrypted connections
if (_settings.EndPointEncryption.Enabled)
{
options
.WithEncryptedEndpoint()
.WithEncryptionSslProtocol(System.Security.Authentication.SslProtocols.Tls12)
.WithEncryptionCertificate(_settings.EndPointEncryption.ReadCertificate());
if (_settings.EndPointEncryption.TryReadIPv4(out var address4))
{
options.WithEncryptedEndpointBoundIPAddress(address4);
}
if (_settings.EndPointEncryption.TryReadIPv6(out var address6))
{
options.WithEncryptedEndpointBoundIPV6Address(address6);
}
if (_settings.EndPoint.Port > 0)
{
options.WithEncryptedEndpointPort(_settings.EndPointEncryption.Port);
}
}
else
{
options.WithoutEncryptedEndpoint();
}
if (_settings.ConnectionBacklog > 0)
{
options.WithConnectionBacklog(_settings.ConnectionBacklog);
}
_mqttServer.ClientConnectedHandler = _mqttClientConnectedHandler; _mqttServer.ClientConnectedHandler = _mqttClientConnectedHandler;
_mqttServer.ClientDisconnectedHandler = _mqttClientDisconnectedHandler; _mqttServer.ClientDisconnectedHandler = _mqttClientDisconnectedHandler;
_mqttServer.ClientSubscribedTopicHandler = _mqttClientSubscribedTopicHandler; _mqttServer.ClientSubscribedTopicHandler = _mqttClientSubscribedTopicHandler;
_mqttServer.ClientUnsubscribedTopicHandler = _mqttClientUnsubscribedTopicHandler; _mqttServer.ClientUnsubscribedTopicHandler = _mqttClientUnsubscribedTopicHandler;
_mqttServer.StartAsync(options.Build()).GetAwaiter().GetResult(); _mqttServer.StartAsync(CreateMqttServerOptions()).GetAwaiter().GetResult();
_logger.LogInformation("MQTT server started."); _logger.LogInformation("MQTT server started.");
} }
public Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
{
return _webSocketServerAdapter.RunWebSocketConnectionAsync(webSocket, httpContext);
}
private void Publish(PythonDictionary parameters) private void Publish(PythonDictionary parameters)
{ {
try try
@@ -187,5 +130,75 @@ namespace MQTTnet.Server.Mqtt
_logger.LogError(exception, "Error while publishing application message from server."); _logger.LogError(exception, "Error while publishing application message from server.");
} }
} }
private IMqttServerOptions CreateMqttServerOptions()
{
var options = new MqttServerOptionsBuilder()
.WithMaxPendingMessagesPerClient(_settings.MaxPendingMessagesPerClient)
.WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(_settings.CommunicationTimeout))
.WithConnectionValidator(_mqttConnectionValidator)
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
.WithStorage(new MqttServerStorage(_settings));
// Configure unencrypted connections
if (_settings.TcpEndPoint.Enabled)
{
options.WithDefaultEndpoint();
if (_settings.TcpEndPoint.TryReadIPv4(out var address4))
{
options.WithDefaultEndpointBoundIPAddress(address4);
}
if (_settings.TcpEndPoint.TryReadIPv6(out var address6))
{
options.WithDefaultEndpointBoundIPV6Address(address6);
}
if (_settings.TcpEndPoint.Port > 0)
{
options.WithDefaultEndpointPort(_settings.TcpEndPoint.Port);
}
}
else
{
options.WithoutDefaultEndpoint();
}
// Configure encrypted connections
if (_settings.EncryptedTcpEndPoint.Enabled)
{
options
.WithEncryptedEndpoint()
.WithEncryptionSslProtocol(System.Security.Authentication.SslProtocols.Tls12)
.WithEncryptionCertificate(_settings.EncryptedTcpEndPoint.ReadCertificate());
if (_settings.EncryptedTcpEndPoint.TryReadIPv4(out var address4))
{
options.WithEncryptedEndpointBoundIPAddress(address4);
}
if (_settings.EncryptedTcpEndPoint.TryReadIPv6(out var address6))
{
options.WithEncryptedEndpointBoundIPV6Address(address6);
}
if (_settings.EncryptedTcpEndPoint.Port > 0)
{
options.WithEncryptedEndpointPort(_settings.EncryptedTcpEndPoint.Port);
}
}
else
{
options.WithoutEncryptedEndpoint();
}
if (_settings.ConnectionBacklog > 0)
{
options.WithConnectionBacklog(_settings.ConnectionBacklog);
}
return options.Build();
}
} }
} }
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Server.Configuration;
namespace MQTTnet.Server.Mqtt
{
public class MqttServerStorage : IMqttServerStorage
{
private readonly SettingsModel _settings;
public MqttServerStorage(SettingsModel settings)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
}
public Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages)
{
return Task.CompletedTask;
}
public Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync()
{
return Task.FromResult<IList<MqttApplicationMessage>>(null);
}
}
}
+68 -38
View File
@@ -2,8 +2,11 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Scripting.Utils;
using MQTTnet.Server.Configuration;
using MQTTnet.Server.Logging; using MQTTnet.Server.Logging;
using MQTTnet.Server.Mqtt; using MQTTnet.Server.Mqtt;
using MQTTnet.Server.Scripting; using MQTTnet.Server.Scripting;
@@ -21,15 +24,16 @@ namespace MQTTnet.Server
Configuration = builder.Build(); Configuration = builder.Build();
} }
public IConfigurationRoot Configuration { get; } public IConfigurationRoot Configuration { get; }
public void Configure( public void Configure(
IApplicationBuilder application, IApplicationBuilder application,
IHostingEnvironment environment, IHostingEnvironment environment,
MqttServerService mqttServerService, MqttServerService mqttServerService,
PythonScriptHostService pythonScriptHostService, PythonScriptHostService pythonScriptHostService,
DataSharingService dataSharingService) DataSharingService dataSharingService,
SettingsModel settings)
{ {
if (environment.IsDevelopment()) if (environment.IsDevelopment())
{ {
@@ -42,38 +46,10 @@ namespace MQTTnet.Server
application.UseStaticFiles(); application.UseStaticFiles();
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
ReceiveBufferSize = 4 * 1024
};
application.UseWebSockets(webSocketOptions);
application.UseHttpsRedirection(); application.UseHttpsRedirection();
application.UseMvc(); application.UseMvc();
application.Use(async (context, next) => ConfigureWebSocketEndpoint(application, mqttServerService, settings);
{
if (context.Request.Path == "/mqtt") // TODO: Full path to config.
{
if (context.WebSockets.IsWebSocketRequest)
{
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false))
{
await mqttServerService.WebSocketServerAdapter.RunWebSocketConnectionAsync(webSocket, context).ConfigureAwait(false);
}
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next().ConfigureAwait(false);
}
});
dataSharingService.Configure(); dataSharingService.Configure();
pythonScriptHostService.Configure(); pythonScriptHostService.Configure();
@@ -84,12 +60,8 @@ namespace MQTTnet.Server
{ {
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// Read settings services.AddSingleton(ReadSettings());
var settings = new Configuration.SettingsModel();
Configuration.Bind("MQTT", settings);
services.AddSingleton(settings);
// Wire up dependencies
services.AddSingleton<PythonIOStream>(); services.AddSingleton<PythonIOStream>();
services.AddSingleton<PythonScriptHostService>(); services.AddSingleton<PythonScriptHostService>();
services.AddSingleton<DataSharingService>(); services.AddSingleton<DataSharingService>();
@@ -106,5 +78,63 @@ namespace MQTTnet.Server
services.AddSingleton<MqttSubscriptionInterceptor>(); services.AddSingleton<MqttSubscriptionInterceptor>();
services.AddSingleton<MqttApplicationMessageInterceptor>(); services.AddSingleton<MqttApplicationMessageInterceptor>();
} }
private SettingsModel ReadSettings()
{
var settings = new Configuration.SettingsModel();
Configuration.Bind("MQTT", settings);
return settings;
}
private static void ConfigureWebSocketEndpoint(
IApplicationBuilder application,
MqttServerService mqttServerService,
SettingsModel settings)
{
if (settings?.WebSocketEndpoint?.Enabled != true)
{
return;
}
if (string.IsNullOrEmpty(settings.WebSocketEndpoint.Path))
{
return;
}
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromSeconds(settings.WebSocketEndpoint.KeepAliveInterval),
ReceiveBufferSize = settings.WebSocketEndpoint.ReceiveBufferSize
};
if (settings.WebSocketEndpoint.AllowedOrigins?.Any() == true)
{
webSocketOptions.AllowedOrigins.AddRange(settings.WebSocketEndpoint.AllowedOrigins);
}
application.UseWebSockets(webSocketOptions);
application.Use(async (context, next) =>
{
if (context.Request.Path == settings.WebSocketEndpoint.Path)
{
if (context.WebSockets.IsWebSocketRequest)
{
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false))
{
await mqttServerService.RunWebSocketConnectionAsync(webSocket, context).ConfigureAwait(false);
}
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next().ConfigureAwait(false);
}
});
}
} }
} }
+16 -4
View File
@@ -16,23 +16,35 @@
localhost - Localhost only localhost - Localhost only
disable - Skip address assignment disable - Skip address assignment
*/ */
"EndPoint": { "TcpEndPoint": {
"Enabled": true, "Enabled": true,
"IPv4": "localhost", "IPv4": "localhost",
"IPv6": "localhost", "IPv6": "localhost",
"Port": 1883 "Port": 1883
}, },
"EndPointEncryption": { "EncryptedTcpEndPoint": {
"Enabled": false, "Enabled": false,
"IPv4": "localhost", "IPv4": "localhost",
"IPv6": "localhost", "IPv6": "localhost",
"Port": 8883, "Port": 8883,
"CertificatePath": "/absolute/path/to/pfx" "CertificatePath": "/absolute/path/to/pfx"
}, },
"CommunicationTimeout": 15, /* In seconds */ "WebSocketEndpoint": {
"Enabled": true,
"Path": "/mqtt",
"KeepAliveInterval": 120, // In seconds.
"ReceiveBufferSize": 4096,
"AllowedOrigins": [] // List of strings with URLs.
},
"CommunicationTimeout": 15, // In seconds.
"ConnectionBacklog": 0, /* Set 0 to disable */ "ConnectionBacklog": 0, /* Set 0 to disable */
"EnablePersistentSessions": false, "EnablePersistentSessions": false,
"MaxPendingMessagesPerClient": 250 "MaxPendingMessagesPerClient": 250,
"RetainedApplicationMessages": {
"Persist": false,
"Filename": "RetainedApplicationMessages.json",
"WriteInterval": 10 // In seconds.
}
}, },
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
@@ -33,13 +33,16 @@ namespace MQTTnet.Server
try try
{ {
var retainedMessages = await _options.Storage.LoadRetainedMessagesAsync().ConfigureAwait(false); var retainedMessages = await _options.Storage.LoadRetainedMessagesAsync().ConfigureAwait(false);
if (retainedMessages?.Any() == true)
using (await _messagesLock.WaitAsync().ConfigureAwait(false))
{ {
_messages.Clear(); using (await _messagesLock.WaitAsync().ConfigureAwait(false))
foreach (var retainedMessage in retainedMessages)
{ {
_messages[retainedMessage.Topic] = retainedMessage; _messages.Clear();
foreach (var retainedMessage in retainedMessages)
{
_messages[retainedMessage.Topic] = retainedMessage;
}
} }
} }
} }