@@ -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,4 +1,4 @@ | |||
namespace MQTTnetServer.Settings | |||
namespace MQTTnet.Server.Configuration | |||
{ | |||
/// <summary> | |||
/// Listen Entry Settings Model |
@@ -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,6 +1,6 @@ | |||
using System.Collections.Generic; | |||
namespace MQTTnetServer.Settings | |||
namespace MQTTnet.Server.Configuration | |||
{ | |||
/// <summary> | |||
/// Main Settings Model |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |