mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-17 01:33:03 +08:00
Extend MQTTnet.Server application.
This commit is contained in:
+1
-1
@@ -47,7 +47,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedC
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnetServer", "Source\MQTTnetServer\MQTTnetServer.csproj", "{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@@ -40,7 +40,6 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public class EndpointConfiguration
|
||||
{
|
||||
public int Port { get; set; } = 1883;
|
||||
|
||||
public int BacklogSize { get; set; } = 10;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace MQTTnetServer.Settings
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Listen Entry Settings Model
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace MQTTnetServer.Settings
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Listen Protocol Types
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public class MqttNetServerConfiguration
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnetServer.Settings
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Main Settings Model
|
||||
+2
-5
@@ -1,10 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MQTTnetServer.Controllers
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Christian Kratky
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Server.Logging
|
||||
{
|
||||
public class MqttNetChildLoggerWrapper : IMqttNetChildLogger
|
||||
{
|
||||
private readonly MqttNetLoggerWrapper _logger;
|
||||
private readonly string _source;
|
||||
|
||||
public MqttNetChildLoggerWrapper(string source, MqttNetLoggerWrapper logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_source = source;
|
||||
}
|
||||
|
||||
public IMqttNetChildLogger CreateChildLogger(string source = null)
|
||||
{
|
||||
return _logger.CreateChildLogger(source);
|
||||
}
|
||||
|
||||
public void Verbose(string message, params object[] parameters)
|
||||
{
|
||||
_logger.Publish(MqttNetLogLevel.Verbose, _source, message, parameters, null);
|
||||
}
|
||||
|
||||
public void Info(string message, params object[] parameters)
|
||||
{
|
||||
_logger.Publish(MqttNetLogLevel.Info, _source, message, parameters, null);
|
||||
}
|
||||
|
||||
public void Warning(Exception exception, string message, params object[] parameters)
|
||||
{
|
||||
_logger.Publish(MqttNetLogLevel.Warning, _source, message, parameters, exception);
|
||||
}
|
||||
|
||||
public void Error(Exception exception, string message, params object[] parameters)
|
||||
{
|
||||
_logger.Publish(MqttNetLogLevel.Error, _source, message, parameters, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Server.Logging
|
||||
{
|
||||
public class MqttNetLoggerWrapper : IMqttNetLogger
|
||||
{
|
||||
private readonly ILogger<MqttServer> _logger;
|
||||
|
||||
public MqttNetLoggerWrapper(ILogger<MqttServer> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
||||
|
||||
public IMqttNetChildLogger CreateChildLogger(string source = null)
|
||||
{
|
||||
return new MqttNetChildLoggerWrapper(source, this);
|
||||
}
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
||||
{
|
||||
var convertedLogLevel = ConvertLogLevel(logLevel);
|
||||
|
||||
if (!_logger.IsEnabled(convertedLogLevel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Log(convertedLogLevel, exception, message, parameters);
|
||||
}
|
||||
|
||||
private static LogLevel ConvertLogLevel(MqttNetLogLevel logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case MqttNetLogLevel.Error: return LogLevel.Error;
|
||||
case MqttNetLogLevel.Warning: return LogLevel.Warning;
|
||||
case MqttNetLogLevel.Info: return LogLevel.Information;
|
||||
case MqttNetLogLevel.Verbose: return LogLevel.Trace;
|
||||
}
|
||||
|
||||
return LogLevel.Debug;
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
-4
@@ -3,11 +3,11 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||
<AssemblyName>MQTTnetServer</AssemblyName>
|
||||
<RootNamespace>MQTTnetServer</RootNamespace>
|
||||
<AssemblyName>MQTTnet.Server</AssemblyName>
|
||||
<RootNamespace>MQTTnet.Server</RootNamespace>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<Company />
|
||||
<Product />
|
||||
<Product>MQTTnet</Product>
|
||||
<Description />
|
||||
<Authors />
|
||||
<PackageId />
|
||||
@@ -16,8 +16,12 @@
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<StartupObject></StartupObject>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>MQTTnetServer.xml</DocumentationFile>
|
||||
<DocumentationFile></DocumentationFile>
|
||||
<WarningsAsErrors>NU1605</WarningsAsErrors>
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
</PropertyGroup>
|
||||
@@ -29,8 +33,12 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IronPython" Version="2.7.9" />
|
||||
<PackageReference Include="IronPython.StdLib" Version="2.7.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="4.0.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -38,4 +46,16 @@
|
||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="LICENSE">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scripts\readme.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scripts\00_sample.py">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MQTTnet.Server.Configuration;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
PrintLogo();
|
||||
|
||||
CreateWebHostBuilder(args).Build().Run();
|
||||
return 0;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintLogo()
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
|
||||
Console.WriteLine(@"
|
||||
__ __ ____ _______ _______ _ _____
|
||||
| \/ |/ __ \__ __|__ __| | | / ____|
|
||||
| \ / | | | | | | | |_ __ ___| |_ | (___ ___ _ ____ _____ _ __
|
||||
| |\/| | | | | | | | | '_ \ / _ \ __| \___ \ / _ \ '__\ \ / / _ \ '__|
|
||||
| | | | |__| | | | | | | | | __/ |_ ____) | __/ | \ V / __/ |
|
||||
|_| |_|\___\_\ |_| |_|_| |_|\___|\__| |_____/ \___|_| \_/ \___|_|");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine(@"
|
||||
-- The official MQTT server implementation of MQTTnet --
|
||||
Copyright (c) 2017-2019 The MQTTnet Team");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
Console.WriteLine(@"
|
||||
https://github.com/chkr1011/MQTTnet");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine(@"
|
||||
Version: 1.0.0-alpha1
|
||||
License: MIT (read LICENSE file)
|
||||
");
|
||||
|
||||
Console.BackgroundColor = ConsoleColor.White;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(" ! THIS IS AN ALPHA VERSION! IT IS NOT RECOMMENDED TO USE IT FOR ANY DIFFERENT PURPOSE THAN TESTING OR EVALUATING!");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
private static IWebHostBuilder CreateWebHostBuilder(string[] args)
|
||||
{
|
||||
var webHost = WebHost.CreateDefaultBuilder(args)
|
||||
.UseKestrel(kestrelOptions =>
|
||||
{
|
||||
kestrelOptions.ListenAnyIP(80, listenOptions =>
|
||||
{
|
||||
listenOptions.NoDelay = true;
|
||||
});
|
||||
});
|
||||
|
||||
//var listen = ReadListenSettings();
|
||||
//webHost
|
||||
// .UseKestrel(o =>
|
||||
// {
|
||||
// if (listen?.Length > 0)
|
||||
// {
|
||||
// foreach (var item in listen)
|
||||
// {
|
||||
// if (item.Address?.Trim() == "*")
|
||||
// {
|
||||
// if (item.Protocol == ListenProtocolTypes.MQTT)
|
||||
// {
|
||||
// o.ListenAnyIP(item.Port, c => c.UseMqtt());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// o.ListenAnyIP(item.Port);
|
||||
// }
|
||||
// }
|
||||
// else if (item.Address?.Trim() == "localhost")
|
||||
// {
|
||||
// if (item.Protocol == ListenProtocolTypes.MQTT)
|
||||
// {
|
||||
// o.ListenLocalhost(item.Port, c => c.UseMqtt());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// o.ListenLocalhost(item.Port);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (IPAddress.TryParse(item.Address, out var ip))
|
||||
// {
|
||||
// if (item.Protocol == ListenProtocolTypes.MQTT)
|
||||
// {
|
||||
// o.Listen(ip, item.Port, c => c.UseMqtt());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// o.Listen(ip, item.Port);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// o.ListenAnyIP(1883, l => l.UseMqtt());
|
||||
// o.ListenAnyIP(5000);
|
||||
// }
|
||||
// });
|
||||
|
||||
webHost.UseStartup<Startup>();
|
||||
|
||||
return webHost;
|
||||
}
|
||||
|
||||
public static ListenModel[] ReadListenSettings()
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
var listen = new List<ListenModel>();
|
||||
builder.Bind("MQTTnetServer:Listen", listen);
|
||||
|
||||
return listen.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MQTTnet.Server.Scripting.DataSharing
|
||||
{
|
||||
public class DataSharingService
|
||||
{
|
||||
private readonly Dictionary<string, object> _storage = new Dictionary<string, object>();
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger<DataSharingService> _logger;
|
||||
|
||||
public DataSharingService(PythonScriptHostService pythonScriptHostService, ILogger<DataSharingService> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
_pythonScriptHostService.RegisterProxyObject("write_shared_data", new Action<string, object>(Write));
|
||||
_pythonScriptHostService.RegisterProxyObject("read_shared_data", new Func<string, object, object>(Read));
|
||||
}
|
||||
|
||||
public void Write(string key, object value)
|
||||
{
|
||||
lock (_storage)
|
||||
{
|
||||
_storage[key] = value;
|
||||
_logger.LogInformation($"Shared data with key '{key}' updated.");
|
||||
}
|
||||
}
|
||||
|
||||
public object Read(string key, object defaultValue)
|
||||
{
|
||||
lock (_storage)
|
||||
{
|
||||
if (!_storage.TryGetValue(key, out var value))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using IronPython.Runtime;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public static class PythonConvert
|
||||
{
|
||||
public static object ToPython(object value)
|
||||
{
|
||||
if (value is PythonDictionary)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is string)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is int)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is float)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is bool)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is IDictionary dictionary)
|
||||
{
|
||||
var pythonDictionary = new PythonDictionary();
|
||||
foreach (DictionaryEntry dictionaryEntry in dictionary)
|
||||
{
|
||||
pythonDictionary.Add(dictionaryEntry.Key, ToPython(dictionaryEntry.Value));
|
||||
}
|
||||
|
||||
return pythonDictionary;
|
||||
}
|
||||
|
||||
if (value is IEnumerable enumerable)
|
||||
{
|
||||
var pythonList = new List();
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
pythonList.Add(ToPython(item));
|
||||
}
|
||||
|
||||
return pythonList;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static string Pythonfy(Enum value)
|
||||
{
|
||||
return Pythonfy(value.ToString());
|
||||
}
|
||||
|
||||
public static string Pythonfy(string value)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
foreach (var @char in value)
|
||||
{
|
||||
if (char.IsUpper(@char) && result.Length > 0)
|
||||
{
|
||||
result.Append('_');
|
||||
}
|
||||
|
||||
result.Append(char.ToLowerInvariant(@char));
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public static TEnum ParseEnum<TEnum>(string value) where TEnum : Enum
|
||||
{
|
||||
return (TEnum)Enum.Parse(typeof(TEnum), value.Replace("_", string.Empty), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public class PythonIOStream : Stream
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly Encoding _encoder = Encoding.UTF8;
|
||||
|
||||
public PythonIOStream(ILogger<PythonIOStream> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var text = _encoder.GetString(buffer, offset, count);
|
||||
if (text.Equals(Environment.NewLine))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogDebug(text);
|
||||
}
|
||||
|
||||
public override bool CanRead { get; } = false;
|
||||
public override bool CanSeek { get; } = false;
|
||||
public override bool CanWrite { get; } = true;
|
||||
public override long Length { get; } = 0L;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Scripting;
|
||||
using Microsoft.Scripting.Hosting;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public class PythonScriptHostService
|
||||
{
|
||||
private readonly IDictionary<string, object> _proxyObjects = new ExpandoObject();
|
||||
private readonly List<PythonScriptInstance> _scriptInstances = new List<PythonScriptInstance>();
|
||||
private readonly ILogger<PythonScriptHostService> _logger;
|
||||
private readonly ScriptEngine _scriptEngine;
|
||||
|
||||
public PythonScriptHostService(PythonIOStream pythonIOStream, ILogger<PythonScriptHostService> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_scriptEngine = IronPython.Hosting.Python.CreateEngine();
|
||||
_scriptEngine.Runtime.IO.SetOutput(pythonIOStream, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
AddSearchPaths(_scriptEngine);
|
||||
|
||||
var scriptsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Scripts");
|
||||
foreach (var filename in Directory.GetFiles(scriptsDirectory, "*.py", SearchOption.AllDirectories).OrderBy(file => file))
|
||||
{
|
||||
TryInitializeScript(filename);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterProxyObject(string name, object action)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||
|
||||
_proxyObjects.Add(name, action);
|
||||
}
|
||||
|
||||
public void InvokeOptionalFunction(string name, object parameters)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
lock (_scriptInstances)
|
||||
{
|
||||
foreach (var pythonScriptInstance in _scriptInstances)
|
||||
{
|
||||
try
|
||||
{
|
||||
pythonScriptInstance.InvokeOptionalFunction(name, parameters);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, $"Error while invoking function '{name}' at script '{pythonScriptInstance.Name}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TryInitializeScript(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
var scriptName = new FileInfo(filename).Name;
|
||||
|
||||
_logger.LogTrace($"Initializing Python script '{scriptName}'...");
|
||||
var code = File.ReadAllText(filename);
|
||||
|
||||
var scriptInstance = CreateScriptInstance(scriptName, code);
|
||||
scriptInstance.InvokeOptionalFunction("initialize");
|
||||
|
||||
_scriptInstances.Add(scriptInstance);
|
||||
|
||||
_logger.LogInformation($"Initialized script '{scriptName}'.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, $"Error while initializing script '{new FileInfo(filename).Name}'.");
|
||||
}
|
||||
}
|
||||
|
||||
private PythonScriptInstance CreateScriptInstance(string name, string code)
|
||||
{
|
||||
var scriptScope = _scriptEngine.CreateScope();
|
||||
|
||||
var source = scriptScope.Engine.CreateScriptSourceFromString(code, SourceCodeKind.File);
|
||||
var compiledCode = source.Compile();
|
||||
|
||||
scriptScope.SetVariable("mqtt_net_server", _proxyObjects);
|
||||
compiledCode.Execute(scriptScope);
|
||||
|
||||
return new PythonScriptInstance(name, scriptScope);
|
||||
}
|
||||
|
||||
private void AddSearchPaths(ScriptEngine scriptEngine)
|
||||
{
|
||||
var paths = new List<string>
|
||||
{
|
||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Lib"),
|
||||
"/usr/lib/python2.7",
|
||||
@"C:\Python27\Lib"
|
||||
};
|
||||
|
||||
AddSearchPaths(scriptEngine, paths);
|
||||
}
|
||||
|
||||
private void AddSearchPaths(ScriptEngine scriptEngine, IEnumerable<string> paths)
|
||||
{
|
||||
var searchPaths = scriptEngine.GetSearchPaths();
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
searchPaths.Add(path);
|
||||
_logger.LogInformation($"Added Python lib path: {path}");
|
||||
}
|
||||
}
|
||||
|
||||
scriptEngine.SetSearchPaths(searchPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Scripting.Hosting;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public class PythonScriptInstance
|
||||
{
|
||||
private readonly ScriptScope _scriptScope;
|
||||
|
||||
public PythonScriptInstance(string name, ScriptScope scriptScope)
|
||||
{
|
||||
_scriptScope = scriptScope;
|
||||
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public bool InvokeOptionalFunction(string name, params object[] parameters)
|
||||
{
|
||||
return InvokeOptionalFunction(name, parameters, out _);
|
||||
}
|
||||
|
||||
public bool InvokeOptionalFunction(string name, object[] parameters, out object result)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
lock (_scriptScope)
|
||||
{
|
||||
if (!_scriptScope.Engine.Operations.TryGetMember(_scriptScope, name, out var member))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(member is PythonFunction function))
|
||||
{
|
||||
throw new Exception($"Member '{name}' is no Python function.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result = _scriptScope.Engine.Operations.Invoke(function, parameters);
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var details = _scriptScope.Engine.GetService<ExceptionOperations>().FormatException(exception);
|
||||
var message = $"Error while invoking function '{name}'. " + Environment.NewLine + details;
|
||||
|
||||
throw new Exception(message, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import json
|
||||
|
||||
|
||||
def initialize():
|
||||
"""
|
||||
This function is invoked after the script file has been loaded.
|
||||
It will be executed only one time.
|
||||
"""
|
||||
|
||||
print("Hello World from Sample script.")
|
||||
|
||||
|
||||
def on_validate_client_connection(context):
|
||||
"""
|
||||
This function is invoked whenever a client wants to connect. It can be used to validate the connection.
|
||||
"""
|
||||
|
||||
print(context)
|
||||
|
||||
mqtt_net_server.write_shared_data(context["client_id"], {"custom_value_1": 1, "custom_value_2": True})
|
||||
|
||||
return
|
||||
|
||||
if context["client_id"] != "test_client":
|
||||
context["result"] = "connection_refused_not_authorized"
|
||||
return
|
||||
|
||||
if context["username"] != "bud spencer":
|
||||
context["result"] = "connection_refused_not_authorized"
|
||||
return
|
||||
|
||||
if context["password"] != "secret":
|
||||
context["result"] = "connection_refused_not_authorized"
|
||||
|
||||
print(context)
|
||||
|
||||
|
||||
def on_intercept_subscription(context):
|
||||
"""
|
||||
This function is invoked whenever a client wants to subscribe to a topic.
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' want's to subscribe to topic '{topic}'.".format(client_id=context["client_id"], topic=context["topic"]))
|
||||
|
||||
|
||||
def on_intercept_application_message(context):
|
||||
"""
|
||||
This function is invoked for every processed application message. It also allows modifying
|
||||
the message or cancel processing at all.
|
||||
"""
|
||||
client_id = context["client_id"]
|
||||
|
||||
if client_id != None:
|
||||
shared_data = mqtt_net_server.read_shared_data(context["client_id"], {})
|
||||
print(shared_data)
|
||||
|
||||
if context["topic"] == "topic_with_response":
|
||||
|
||||
json_payload = {
|
||||
"hello": "world",
|
||||
"x": 1,
|
||||
"y": True,
|
||||
"z": None
|
||||
}
|
||||
|
||||
application_message = {
|
||||
"retain": False,
|
||||
"topic": "reply",
|
||||
"payload": json.dumps(json_payload)
|
||||
}
|
||||
|
||||
mqtt_net_server.publish(application_message)
|
||||
|
||||
print("Client '{client_id}' published topic '{topic}'.".format(client_id=context["client_id"], topic=context["topic"]))
|
||||
@@ -0,0 +1,19 @@
|
||||
This directory contains scripts which are loaded by the server and can be used to perform the following tasks.
|
||||
|
||||
1. Validation of client connections via credentials, client IDs etc.
|
||||
2. Manipulation of every processed message.
|
||||
3. Validation of subscription attempts.
|
||||
4. Publishing of custom application messages.
|
||||
|
||||
All scripts are loaded and _MQTTnet Server_ will invoke functions according to predefined naming conventions.
|
||||
If a function is implemented in multiple scripts the context will be moved throug all instances. This allows overriding of results or passing data to other (following) scripts.
|
||||
|
||||
The Python starndard library ships with _MQTTnet Server_. But it is possible to add custom paths with Python libraries.
|
||||
|
||||
```
|
||||
import sys
|
||||
sys.path.append(PATH_TO_LIBRARY)
|
||||
```
|
||||
|
||||
* All scripts must have the file extension _.py_.
|
||||
* All scripts are sorted alphabetically (A to Z) before being loaded and parsed.
|
||||
@@ -0,0 +1,69 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Server.Logging;
|
||||
using MQTTnet.Server.Mqtt;
|
||||
using MQTTnet.Server.Scripting;
|
||||
using MQTTnet.Server.Scripting.DataSharing;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
//var builder = new ConfigurationBuilder()
|
||||
// .AddJsonFile("appsettings.json")
|
||||
// .AddEnvironmentVariables();
|
||||
|
||||
//Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
public void Configure(
|
||||
IApplicationBuilder application,
|
||||
IHostingEnvironment environment,
|
||||
MqttServerService mqttServerService,
|
||||
PythonScriptHostService pythonScriptHostService,
|
||||
DataSharingService dataSharingService)
|
||||
{
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
application.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
application.UseHsts();
|
||||
}
|
||||
|
||||
application.UseHttpsRedirection();
|
||||
application.UseMvc();
|
||||
|
||||
dataSharingService.Configure();
|
||||
pythonScriptHostService.Configure();
|
||||
mqttServerService.Configure();
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<MqttNetLoggerWrapper>();
|
||||
|
||||
services.AddSingleton<PythonIOStream>();
|
||||
services.AddSingleton<PythonScriptHostService>();
|
||||
services.AddSingleton<DataSharingService>();
|
||||
|
||||
services.AddSingleton<MqttWebSocketServerAdapter>();
|
||||
services.AddSingleton<IMqttServerFactory, MqttFactory>();
|
||||
services.AddSingleton<MqttServerService>();
|
||||
services.AddSingleton<MqttConnectionValidator>();
|
||||
services.AddSingleton<MqttSubscriptionInterceptor>();
|
||||
services.AddSingleton<MqttApplicationMessageInterceptor>();
|
||||
|
||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,17 @@ namespace MQTTnet
|
||||
{
|
||||
public class MqttFactory : IMqttClientFactory, IMqttServerFactory
|
||||
{
|
||||
private readonly IMqttNetLogger _logger;
|
||||
|
||||
public MqttFactory() : this(new MqttNetLogger())
|
||||
{
|
||||
}
|
||||
|
||||
public MqttFactory(IMqttNetLogger logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public IMqttClient CreateMqttClient()
|
||||
{
|
||||
return CreateMqttClient(new MqttNetLogger());
|
||||
@@ -57,5 +68,12 @@ namespace MQTTnet
|
||||
|
||||
return new MqttServer(adapters, logger.CreateChildLogger());
|
||||
}
|
||||
|
||||
public IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> adapters)
|
||||
{
|
||||
if (adapters == null) throw new ArgumentNullException(nameof(adapters));
|
||||
|
||||
return new MqttServer(adapters, _logger.CreateChildLogger());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>MQTTnetServer</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:MQTTnetServer.Program">
|
||||
<summary>
|
||||
Main Entry point
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:MQTTnetServer.Program.Main(System.String[])">
|
||||
<summary>
|
||||
Main
|
||||
</summary>
|
||||
<param name="args"></param>
|
||||
</member>
|
||||
<member name="M:MQTTnetServer.Program.CreateWebHostBuilder(System.String[])">
|
||||
<summary>
|
||||
Configure and Start Kestrel
|
||||
</summary>
|
||||
<param name="args"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:MQTTnetServer.Program.ReadListenSettings">
|
||||
<summary>
|
||||
Read Application Settings
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:MQTTnetServer.Settings.ListenModel">
|
||||
<summary>
|
||||
Listen Entry Settings Model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:MQTTnetServer.Settings.ListenModel.#ctor">
|
||||
<summary>
|
||||
Constructor
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:MQTTnetServer.Settings.ListenModel.Address">
|
||||
<summary>
|
||||
Listen Address
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:MQTTnetServer.Settings.ListenModel.Port">
|
||||
<summary>
|
||||
Listen Port
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:MQTTnetServer.Settings.ListenModel.Protocol">
|
||||
<summary>
|
||||
Protocol Type
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:MQTTnetServer.Settings.ListenProtocolTypes">
|
||||
<summary>
|
||||
Listen Protocol Types
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:MQTTnetServer.Settings.ListenProtocolTypes.HTTP">
|
||||
<summary>
|
||||
HTTP
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:MQTTnetServer.Settings.ListenProtocolTypes.HTTPS">
|
||||
<summary>
|
||||
HTTPS
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:MQTTnetServer.Settings.ListenProtocolTypes.MQTT">
|
||||
<summary>
|
||||
MQTT
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:MQTTnetServer.Settings.SettingsModel">
|
||||
<summary>
|
||||
Main Settings Model
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:MQTTnetServer.Settings.SettingsModel.Listen">
|
||||
<summary>
|
||||
Listen Settings
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:MQTTnetServer.Startup">
|
||||
<summary>
|
||||
Web App Startup
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:MQTTnetServer.Startup.#ctor(Microsoft.Extensions.Configuration.IConfiguration)">
|
||||
<summary>
|
||||
Constructor
|
||||
</summary>
|
||||
<param name="configuration"></param>
|
||||
</member>
|
||||
<member name="P:MQTTnetServer.Startup.Configuration">
|
||||
<summary>
|
||||
Application Settings
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:MQTTnetServer.Startup.Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder,Microsoft.AspNetCore.Hosting.IHostingEnvironment)">
|
||||
<summary>
|
||||
This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
</summary>
|
||||
<param name="app"></param>
|
||||
<param name="env"></param>
|
||||
</member>
|
||||
<member name="M:MQTTnetServer.Startup.ConfigureServices(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
|
||||
<summary>
|
||||
This method gets called by the runtime. Use this method to add services to the container.
|
||||
</summary>
|
||||
<param name="services"></param>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
@@ -1,118 +0,0 @@
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnetServer.Settings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnetServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Main Entry point
|
||||
/// </summary>
|
||||
public class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// Main
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
CreateWebHostBuilder(args).Build().Run();
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
Console.WriteLine("Could not find application settings file in: " + e.FileName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure and Start Kestrel
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
|
||||
{
|
||||
var webHost = WebHost.CreateDefaultBuilder(args);
|
||||
var listen = ReadListenSettings();
|
||||
webHost
|
||||
.UseKestrel(o =>
|
||||
{
|
||||
if (listen?.Length > 0)
|
||||
{
|
||||
foreach (var item in listen)
|
||||
{
|
||||
if (item.Address?.Trim() == "*")
|
||||
{
|
||||
if (item.Protocol == ListenProtocolTypes.MQTT)
|
||||
{
|
||||
o.ListenAnyIP(item.Port, c => c.UseMqtt());
|
||||
}
|
||||
else
|
||||
{
|
||||
o.ListenAnyIP(item.Port);
|
||||
}
|
||||
}
|
||||
else if (item.Address?.Trim() == "localhost")
|
||||
{
|
||||
if (item.Protocol == ListenProtocolTypes.MQTT)
|
||||
{
|
||||
o.ListenLocalhost(item.Port, c => c.UseMqtt());
|
||||
}
|
||||
else
|
||||
{
|
||||
o.ListenLocalhost(item.Port);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IPAddress.TryParse(item.Address, out var ip))
|
||||
{
|
||||
if (item.Protocol == ListenProtocolTypes.MQTT)
|
||||
{
|
||||
o.Listen(ip, item.Port, c => c.UseMqtt());
|
||||
}
|
||||
else
|
||||
{
|
||||
o.Listen(ip, item.Port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
o.ListenAnyIP(1883, l => l.UseMqtt());
|
||||
o.ListenAnyIP(5000);
|
||||
}
|
||||
});
|
||||
|
||||
webHost.UseStartup<Startup>();
|
||||
|
||||
return webHost;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read Application Settings
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static ListenModel[] ReadListenSettings()
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
var listen = new List<ListenModel>();
|
||||
builder.Bind("MQTTnetServer:Listen", listen);
|
||||
|
||||
return listen.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace MQTTnetServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Web App Startup
|
||||
/// </summary>
|
||||
public class Startup
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="configuration"></param>
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddEnvironmentVariables();
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Application Settings
|
||||
/// </summary>
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="env"></param>
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseMvc();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method gets called by the runtime. Use this method to add services to the container.
|
||||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user