From 8888a366392d1bd2c715b3852923a4dabe3a9b0a Mon Sep 17 00:00:00 2001 From: Jeff Scherrer Date: Fri, 30 Dec 2022 16:41:00 -0800 Subject: [PATCH 01/16] Hosting extensions initial check-in. --- MQTTnet.sln | 22 +++-- Samples/MQTTnet.Samples.csproj | 1 + .../Server_Hosting_Extensions_Samples.cs | 98 +++++++++++++++++++ .../MQTTnet.Hosting/HostBuilderExtensions.cs | 20 ++++ .../HostingMqttServerOptionsBuilder.cs | 22 +++++ Source/MQTTnet.Hosting/MQTTnet.Hosting.csproj | 19 ++++ 6 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 Samples/Server/Server_Hosting_Extensions_Samples.cs create mode 100644 Source/MQTTnet.Hosting/HostBuilderExtensions.cs create mode 100644 Source/MQTTnet.Hosting/HostingMqttServerOptionsBuilder.cs create mode 100644 Source/MQTTnet.Hosting/MQTTnet.Hosting.csproj diff --git a/MQTTnet.sln b/MQTTnet.sln index 34abf629f..a8a0dc104 100644 --- a/MQTTnet.sln +++ b/MQTTnet.sln @@ -7,9 +7,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\M EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}" ProjectSection(SolutionItems) = preProject + CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md LICENSE = LICENSE README.md = README.md - CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}" @@ -20,17 +20,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedC EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{2BD01D53-4CA5-4142-BE8D-313876395E3E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Samples", "Samples\MQTTnet.Samples.csproj", "{71CF35F5-3327-4A91-AAF4-5340F6701771}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Samples", "Samples\MQTTnet.Samples.csproj", "{71CF35F5-3327-4A91-AAF4-5340F6701771}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Tests", "Source\MQTTnet.Tests\MQTTnet.Tests.csproj", "{B270F32A-9F3E-42EE-A989-813E35E29ADB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Source\MQTTnet.Tests\MQTTnet.Tests.csproj", "{B270F32A-9F3E-42EE-A989-813E35E29ADB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspNetCore.Tests", "Source\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{A238BBBF-C75F-482D-9CC3-BB34ABA9B675}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Source\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{A238BBBF-C75F-482D-9CC3-BB34ABA9B675}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Benchmarks", "Source\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Source\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp", "Source\MQTTnet.TestApp\MQTTnet.TestApp.csproj", "{175D5340-CC5B-4542-939D-4E7D15A0BC8D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp", "Source\MQTTnet.TestApp\MQTTnet.TestApp.csproj", "{175D5340-CC5B-4542-939D-4E7D15A0BC8D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspTestApp", "Source\MQTTnet.AspTestApp\MQTTnet.AspTestApp.csproj", "{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspTestApp", "Source\MQTTnet.AspTestApp\MQTTnet.AspTestApp.csproj", "{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Hosting", "Source\MQTTnet.Hosting\MQTTnet.Hosting.csproj", "{B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -82,12 +84,14 @@ Global {72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU {72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU {72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894} EndGlobalSection diff --git a/Samples/MQTTnet.Samples.csproj b/Samples/MQTTnet.Samples.csproj index dcc1ad06e..fc61f512b 100644 --- a/Samples/MQTTnet.Samples.csproj +++ b/Samples/MQTTnet.Samples.csproj @@ -18,6 +18,7 @@ + diff --git a/Samples/Server/Server_Hosting_Extensions_Samples.cs b/Samples/Server/Server_Hosting_Extensions_Samples.cs new file mode 100644 index 000000000..d6915a42a --- /dev/null +++ b/Samples/Server/Server_Hosting_Extensions_Samples.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable InconsistentNaming +// ReSharper disable EmptyConstructor +// ReSharper disable MemberCanBeMadeStatic.Local + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using MQTTnet.AspNetCore; +using MQTTnet.Server; + +namespace MQTTnet.Samples.Server; + +public static class Server_Hosting_Extensions_Samples +{ + public static Task Start_Server() + { + var builder = new HostBuilder(); + + builder + .UseHostedMqttServerWithServices(mqttServer => + { + + }); + + var host = builder.Build(); + return host.RunAsync(); + } + + //sealed class MqttController + //{ + // public MqttController() + // { + // // Inject other services via constructor. + // } + + // public Task OnClientConnected(ClientConnectedEventArgs eventArgs) + // { + // Console.WriteLine($"Client '{eventArgs.ClientId}' connected."); + // return Task.CompletedTask; + // } + + + // public Task ValidateConnection(ValidatingConnectionEventArgs eventArgs) + // { + // Console.WriteLine($"Client '{eventArgs.ClientId}' wants to connect. Accepting!"); + // return Task.CompletedTask; + // } + //} + + sealed class Startup + { + //public void Configure(IApplicationBuilder app, IWebHostEnvironment environment, MqttController mqttController) + //{ + // app.UseRouting(); + + // app.UseEndpoints( + // endpoints => + // { + // endpoints.MapConnectionHandler( + // "/mqtt", + // httpConnectionDispatcherOptions => httpConnectionDispatcherOptions.WebSockets.SubProtocolSelector = + // protocolList => protocolList.FirstOrDefault() ?? string.Empty); + // }); + + // app.UseMqttServer( + // server => + // { + // /* + // * Attach event handlers etc. if required. + // */ + + // server.ValidatingConnectionAsync += mqttController.ValidateConnection; + // server.ClientConnectedAsync += mqttController.OnClientConnected; + // }); + //} + + public void ConfigureServices(IServiceCollection services) + { + services.AddHostedMqttServer( + optionsBuilder => + { + optionsBuilder.WithDefaultEndpoint(); + }); + + services.AddMqttConnectionHandler(); + services.AddConnections(); + + //services.AddSingleton(); + } + } +} \ No newline at end of file diff --git a/Source/MQTTnet.Hosting/HostBuilderExtensions.cs b/Source/MQTTnet.Hosting/HostBuilderExtensions.cs new file mode 100644 index 000000000..2eb65f10b --- /dev/null +++ b/Source/MQTTnet.Hosting/HostBuilderExtensions.cs @@ -0,0 +1,20 @@ +using MQTTnet.Hosting; +using System; + +namespace Microsoft.Extensions.Hosting +{ + public static class HostBuilderExtensions + { + + public static IHostBuilder UseHostedMqttServerWithServices(this IHostBuilder hostBuilder, Action configure) + { + hostBuilder.ConfigureServices((context, services) => + { + + }); + + return hostBuilder; + } + + } +} diff --git a/Source/MQTTnet.Hosting/HostingMqttServerOptionsBuilder.cs b/Source/MQTTnet.Hosting/HostingMqttServerOptionsBuilder.cs new file mode 100644 index 000000000..9315d4c7c --- /dev/null +++ b/Source/MQTTnet.Hosting/HostingMqttServerOptionsBuilder.cs @@ -0,0 +1,22 @@ +using MQTTnet.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MQTTnet.Hosting +{ + public class HostingMqttServerOptionsBuilder : MqttServerOptionsBuilder + { + private readonly IServiceProvider _serviceProvider; + + public HostingMqttServerOptionsBuilder(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public IServiceProvider ServiceProvider => _serviceProvider; + + } +} diff --git a/Source/MQTTnet.Hosting/MQTTnet.Hosting.csproj b/Source/MQTTnet.Hosting/MQTTnet.Hosting.csproj new file mode 100644 index 000000000..da7acdb4e --- /dev/null +++ b/Source/MQTTnet.Hosting/MQTTnet.Hosting.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1;net5.0;net6.0;net7.0 + enable + + + + + + + + + + + + + + From 392bd3957003331ab2aebeab7c9e8f5fa3321912 Mon Sep 17 00:00:00 2001 From: Jeff Scherrer Date: Fri, 30 Dec 2022 18:42:14 -0800 Subject: [PATCH 02/16] Added hosting extensions capability. --- Samples/Server/Server_Asp_Net_Samples.cs | 2 +- .../Server_Hosting_Extensions_Samples.cs | 69 +-------- .../MQTTnet.Hosting/HostBuilderExtensions.cs | 36 ++++- .../HostingMqttServerOptionsBuilder.cs | 22 --- Source/MQTTnet.Hosting/MqttHostedServer.cs | 31 ++++ .../MqttServerConfigurationHostedService.cs | 37 +++++ .../MqttServerHostingBuilder.cs | 138 ++++++++++++++++++ Source/MQTTnet.Tests/MQTTnet.Tests.csproj | 25 ++++ Source/MQTTnet.Tests/Server/Hosting_Tests.cs | 122 ++++++++++++++++ 9 files changed, 390 insertions(+), 92 deletions(-) delete mode 100644 Source/MQTTnet.Hosting/HostingMqttServerOptionsBuilder.cs create mode 100644 Source/MQTTnet.Hosting/MqttHostedServer.cs create mode 100644 Source/MQTTnet.Hosting/MqttServerConfigurationHostedService.cs create mode 100644 Source/MQTTnet.Hosting/MqttServerHostingBuilder.cs create mode 100644 Source/MQTTnet.Tests/Server/Hosting_Tests.cs diff --git a/Samples/Server/Server_Asp_Net_Samples.cs b/Samples/Server/Server_Asp_Net_Samples.cs index d085196d4..24e4244d1 100644 --- a/Samples/Server/Server_Asp_Net_Samples.cs +++ b/Samples/Server/Server_Asp_Net_Samples.cs @@ -87,7 +87,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment environment, /* * Attach event handlers etc. if required. */ - + server.ValidatingConnectionAsync += mqttController.ValidateConnection; server.ClientConnectedAsync += mqttController.OnClientConnected; }); diff --git a/Samples/Server/Server_Hosting_Extensions_Samples.cs b/Samples/Server/Server_Hosting_Extensions_Samples.cs index d6915a42a..f4129810e 100644 --- a/Samples/Server/Server_Hosting_Extensions_Samples.cs +++ b/Samples/Server/Server_Hosting_Extensions_Samples.cs @@ -24,75 +24,12 @@ public static Task Start_Server() var builder = new HostBuilder(); builder - .UseHostedMqttServerWithServices(mqttServer => + .UseMqttServer(mqtt => { - + mqtt.WithDefaultEndpoint(); }); var host = builder.Build(); - return host.RunAsync(); - } - - //sealed class MqttController - //{ - // public MqttController() - // { - // // Inject other services via constructor. - // } - - // public Task OnClientConnected(ClientConnectedEventArgs eventArgs) - // { - // Console.WriteLine($"Client '{eventArgs.ClientId}' connected."); - // return Task.CompletedTask; - // } - - - // public Task ValidateConnection(ValidatingConnectionEventArgs eventArgs) - // { - // Console.WriteLine($"Client '{eventArgs.ClientId}' wants to connect. Accepting!"); - // return Task.CompletedTask; - // } - //} - - sealed class Startup - { - //public void Configure(IApplicationBuilder app, IWebHostEnvironment environment, MqttController mqttController) - //{ - // app.UseRouting(); - - // app.UseEndpoints( - // endpoints => - // { - // endpoints.MapConnectionHandler( - // "/mqtt", - // httpConnectionDispatcherOptions => httpConnectionDispatcherOptions.WebSockets.SubProtocolSelector = - // protocolList => protocolList.FirstOrDefault() ?? string.Empty); - // }); - - // app.UseMqttServer( - // server => - // { - // /* - // * Attach event handlers etc. if required. - // */ - - // server.ValidatingConnectionAsync += mqttController.ValidateConnection; - // server.ClientConnectedAsync += mqttController.OnClientConnected; - // }); - //} - - public void ConfigureServices(IServiceCollection services) - { - services.AddHostedMqttServer( - optionsBuilder => - { - optionsBuilder.WithDefaultEndpoint(); - }); - - services.AddMqttConnectionHandler(); - services.AddConnections(); - - //services.AddSingleton(); - } + return host.RunAsync(); } } \ No newline at end of file diff --git a/Source/MQTTnet.Hosting/HostBuilderExtensions.cs b/Source/MQTTnet.Hosting/HostBuilderExtensions.cs index 2eb65f10b..498688751 100644 --- a/Source/MQTTnet.Hosting/HostBuilderExtensions.cs +++ b/Source/MQTTnet.Hosting/HostBuilderExtensions.cs @@ -1,18 +1,48 @@ +using Microsoft.Extensions.DependencyInjection; +using MQTTnet.Adapter; +using MQTTnet.Diagnostics; using MQTTnet.Hosting; +using MQTTnet.Implementations; +using MQTTnet.Server; using System; +using System.Collections.Generic; namespace Microsoft.Extensions.Hosting { public static class HostBuilderExtensions { - public static IHostBuilder UseHostedMqttServerWithServices(this IHostBuilder hostBuilder, Action configure) + public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder) + => UseMqttServer(hostBuilder, builder => + { + builder.WithDefaultEndpoint(); + }); + + + public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder, Action configure) { + var configureActions = new List>(); hostBuilder.ConfigureServices((context, services) => { - - }); + services.AddSingleton(s => + { + var builder = new MqttServerHostingBuilder(s, configureActions); + configure(builder); + return builder.Build(); + }); + var logger = new MqttNetEventLogger(); + + services.AddSingleton(logger); + services.AddSingleton(); + services.AddSingleton(s => s.GetRequiredService()); + services.AddSingleton(s => new MqttServerConfigurationHostedService(s, configureActions)); + services.AddSingleton(s => s.GetRequiredService()); + + services.AddSingleton(); + services.AddSingleton(s => s.GetService()); + + }); return hostBuilder; } diff --git a/Source/MQTTnet.Hosting/HostingMqttServerOptionsBuilder.cs b/Source/MQTTnet.Hosting/HostingMqttServerOptionsBuilder.cs deleted file mode 100644 index 9315d4c7c..000000000 --- a/Source/MQTTnet.Hosting/HostingMqttServerOptionsBuilder.cs +++ /dev/null @@ -1,22 +0,0 @@ -using MQTTnet.Server; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MQTTnet.Hosting -{ - public class HostingMqttServerOptionsBuilder : MqttServerOptionsBuilder - { - private readonly IServiceProvider _serviceProvider; - - public HostingMqttServerOptionsBuilder(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public IServiceProvider ServiceProvider => _serviceProvider; - - } -} diff --git a/Source/MQTTnet.Hosting/MqttHostedServer.cs b/Source/MQTTnet.Hosting/MqttHostedServer.cs new file mode 100644 index 000000000..3a0eec591 --- /dev/null +++ b/Source/MQTTnet.Hosting/MqttHostedServer.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using System.Threading; +using MQTTnet.Server; +using MQTTnet.Adapter; +using MQTTnet.Diagnostics; + +namespace MQTTnet.Hosting +{ + public sealed class MqttHostedServer : MqttServer, IHostedService + { + public MqttHostedServer(MqttServerOptions options, IEnumerable adapters, IMqttNetLogger logger) + : base(options, adapters, logger) + { + } + + public Task StartAsync(CancellationToken cancellationToken) + { + _ = StartAsync(); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return StopAsync(); + } + } +} diff --git a/Source/MQTTnet.Hosting/MqttServerConfigurationHostedService.cs b/Source/MQTTnet.Hosting/MqttServerConfigurationHostedService.cs new file mode 100644 index 000000000..9ebe65d9c --- /dev/null +++ b/Source/MQTTnet.Hosting/MqttServerConfigurationHostedService.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using MQTTnet.Server; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace MQTTnet.Hosting +{ + public class MqttServerConfigurationHostedService : IHostedService + { + private readonly IServiceProvider _serviceProvider; + private readonly List> _configureActions; + + public MqttServerConfigurationHostedService(IServiceProvider serviceProvider, List> configureActions) + { + _serviceProvider = serviceProvider; + _configureActions = configureActions; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + var server = _serviceProvider.GetRequiredService(); + _configureActions.ForEach(a => a(server)); + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + + return Task.CompletedTask; + } + } +} diff --git a/Source/MQTTnet.Hosting/MqttServerHostingBuilder.cs b/Source/MQTTnet.Hosting/MqttServerHostingBuilder.cs new file mode 100644 index 000000000..8b671a38b --- /dev/null +++ b/Source/MQTTnet.Hosting/MqttServerHostingBuilder.cs @@ -0,0 +1,138 @@ +using Microsoft.Extensions.DependencyInjection; +using MQTTnet.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MQTTnet.Hosting +{ + public class MqttServerHostingBuilder : MqttServerOptionsBuilder + { + private readonly List> _configureActions; + + public MqttServerHostingBuilder(IServiceProvider serviceProvider, List> configureActions) + { + ServiceProvider = serviceProvider; + _configureActions = configureActions; + } + + public event Func ApplicationMessageNotConsumedAsync + { + add => _configureActions.Add(server => server.ApplicationMessageNotConsumedAsync += value); + remove => _configureActions.Add(server => server.ApplicationMessageNotConsumedAsync -= value); + } + + public event Func ClientAcknowledgedPublishPacketAsync + { + add => _configureActions.Add(server => server.ClientAcknowledgedPublishPacketAsync += value); + remove => _configureActions.Add(server => server.ClientAcknowledgedPublishPacketAsync -= value); + } + + public event Func ClientConnectedAsync + { + add => _configureActions.Add(server => server.ClientConnectedAsync += value); + remove => _configureActions.Add(server => server.ClientConnectedAsync -= value); + } + + public event Func ClientDisconnectedAsync + { + add => _configureActions.Add(server => server.ClientDisconnectedAsync += value); + remove => _configureActions.Add(server => server.ClientDisconnectedAsync -= value); + } + + public event Func ClientSubscribedTopicAsync + { + add => _configureActions.Add(server => server.ClientSubscribedTopicAsync += value); + remove => _configureActions.Add(server => server.ClientSubscribedTopicAsync -= value); + } + + public event Func ClientUnsubscribedTopicAsync + { + add => _configureActions.Add(server => server.ClientUnsubscribedTopicAsync += value); + remove => _configureActions.Add(server => server.ClientUnsubscribedTopicAsync -= value); + } + + public event Func InterceptingInboundPacketAsync + { + add => _configureActions.Add(server => server.InterceptingInboundPacketAsync += value); + remove => _configureActions.Add(server => server.InterceptingInboundPacketAsync -= value); + } + + public event Func InterceptingOutboundPacketAsync + { + add => _configureActions.Add(server => server.InterceptingOutboundPacketAsync += value); + remove => _configureActions.Add(server => server.InterceptingOutboundPacketAsync -= value); + } + + public event Func InterceptingPublishAsync + { + add => _configureActions.Add(server => server.InterceptingPublishAsync += value); + remove => _configureActions.Add(server => server.InterceptingPublishAsync -= value); + } + + public event Func InterceptingSubscriptionAsync + { + add => _configureActions.Add(server => server.InterceptingSubscriptionAsync += value); + remove => _configureActions.Add(server => server.InterceptingSubscriptionAsync -= value); + } + + public event Func InterceptingUnsubscriptionAsync + { + add => _configureActions.Add(server => server.InterceptingUnsubscriptionAsync += value); + remove => _configureActions.Add(server => server.InterceptingUnsubscriptionAsync -= value); + } + + public event Func LoadingRetainedMessageAsync + { + add => _configureActions.Add(server => server.LoadingRetainedMessageAsync += value); + remove => _configureActions.Add(server => server.LoadingRetainedMessageAsync -= value); + } + + public event Func PreparingSessionAsync + { + add => _configureActions.Add(server => server.PreparingSessionAsync += value); + remove => _configureActions.Add(server => server.PreparingSessionAsync -= value); + } + + public event Func RetainedMessageChangedAsync + { + add => _configureActions.Add(server => server.RetainedMessageChangedAsync += value); + remove => _configureActions.Add(server => server.RetainedMessageChangedAsync -= value); + } + + public event Func RetainedMessagesClearedAsync + { + add => _configureActions.Add(server => server.RetainedMessagesClearedAsync += value); + remove => _configureActions.Add(server => server.RetainedMessagesClearedAsync -= value); + } + + public event Func SessionDeletedAsync + { + add => _configureActions.Add(server => server.SessionDeletedAsync += value); + remove => _configureActions.Add(server => server.SessionDeletedAsync -= value); + } + + public event Func StartedAsync + { + add => _configureActions.Add(server => server.StartedAsync += value); + remove => _configureActions.Add(server => server.StartedAsync -= value); + } + + public event Func StoppedAsync + { + add => _configureActions.Add(server => server.StoppedAsync += value); + remove => _configureActions.Add(server => server.StoppedAsync -= value); + } + + public event Func ValidatingConnectionAsync + { + add => _configureActions.Add(server => server.ValidatingConnectionAsync += value); + remove => _configureActions.Add(server => server.ValidatingConnectionAsync -= value); + } + + public IServiceProvider ServiceProvider { get; } + + } +} diff --git a/Source/MQTTnet.Tests/MQTTnet.Tests.csproj b/Source/MQTTnet.Tests/MQTTnet.Tests.csproj index 36720c6b2..5f508b62a 100644 --- a/Source/MQTTnet.Tests/MQTTnet.Tests.csproj +++ b/Source/MQTTnet.Tests/MQTTnet.Tests.csproj @@ -21,6 +21,31 @@ + + + + + + 3.1.0 + + + + + + 5.0.0 + + + + + + 6.0.0 + + + + + + 7.0.0 + \ No newline at end of file diff --git a/Source/MQTTnet.Tests/Server/Hosting_Tests.cs b/Source/MQTTnet.Tests/Server/Hosting_Tests.cs new file mode 100644 index 000000000..1edc37e87 --- /dev/null +++ b/Source/MQTTnet.Tests/Server/Hosting_Tests.cs @@ -0,0 +1,122 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MQTTnet.Client; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Threading; +#if NET5_0_OR_GREATER +using Microsoft.Extensions.Hosting; + +namespace MQTTnet.Tests.Server +{ + + [TestClass] + public class Hosting_Tests + { + + [TestMethod] + public async Task Default_Host_Configuration() + { + + var host = new HostBuilder() + .UseMqttServer() + .Build(); + await host.StartAsync(); + + // Perform client connect test + try + { + var factory = new MqttFactory(); + var client = factory.CreateMqttClient(); + var options = factory.CreateClientOptionsBuilder(); + options + .WithTcpServer("127.0.0.1"); + + await client.ConnectAsync(options.Build()); + } + finally + { + await host.StopAsync(); + } + } + + [TestMethod] + public async Task Custom_Host_Configuration() + { + + var host = new HostBuilder() + .UseMqttServer(mqtt => + { + mqtt + .WithDefaultEndpoint() + .WithKeepAlive(); + }) + .Build(); + await host.StartAsync(); + + // Perform client connect test + try + { + var factory = new MqttFactory(); + var client = factory.CreateMqttClient(); + var options = factory.CreateClientOptionsBuilder(); + options + .WithTcpServer("127.0.0.1"); + + await client.ConnectAsync(options.Build()); + } + finally + { + await host.StopAsync(); + } + } + + [TestMethod] + public async Task Advanced_Host_Configuration() + { + var syncLock = new object(); + var connectedClientCount = 0; + var host = new HostBuilder() + .UseMqttServer(mqtt => + { + mqtt + .WithDefaultEndpoint() + .WithKeepAlive(); + + mqtt.ClientConnectedAsync += e => + { + lock (syncLock) + { + connectedClientCount++; + } + return Task.CompletedTask; + }; + }) + .Build(); + + await host.StartAsync(); + + // Perform client connect test + try + { + var factory = new MqttFactory(); + var client = factory.CreateMqttClient(); + var options = factory.CreateClientOptionsBuilder(); + options + .WithTcpServer("127.0.0.1"); + + await client.ConnectAsync(options.Build()); + } + finally + { + await host.StopAsync(); + } + Assert.AreEqual(1, connectedClientCount); + } + + } +} + +#endif \ No newline at end of file From 2055653ee03e8d1e1f8f6e23728f722d6fb71ed8 Mon Sep 17 00:00:00 2001 From: Jeff Scherrer Date: Fri, 30 Dec 2022 19:33:13 -0800 Subject: [PATCH 03/16] Added simple server example. --- Samples/Server/Server_Hosting_Extensions_Samples.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Samples/Server/Server_Hosting_Extensions_Samples.cs b/Samples/Server/Server_Hosting_Extensions_Samples.cs index f4129810e..569351842 100644 --- a/Samples/Server/Server_Hosting_Extensions_Samples.cs +++ b/Samples/Server/Server_Hosting_Extensions_Samples.cs @@ -19,6 +19,16 @@ namespace MQTTnet.Samples.Server; public static class Server_Hosting_Extensions_Samples { + + public static Task Start_Simple_Server() + { + var host = new HostBuilder() + .UseMqttServer() + .Build(); + + return host.RunAsync(); + } + public static Task Start_Server() { var builder = new HostBuilder(); From 5c9df71aa764637799a92e5ac2fd6b06b8eae425 Mon Sep 17 00:00:00 2001 From: Jeff Scherrer Date: Fri, 30 Dec 2022 21:33:45 -0800 Subject: [PATCH 04/16] Added single line example. --- Samples/Server/Server_Hosting_Extensions_Samples.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Samples/Server/Server_Hosting_Extensions_Samples.cs b/Samples/Server/Server_Hosting_Extensions_Samples.cs index 569351842..840c37751 100644 --- a/Samples/Server/Server_Hosting_Extensions_Samples.cs +++ b/Samples/Server/Server_Hosting_Extensions_Samples.cs @@ -8,11 +8,8 @@ // ReSharper disable EmptyConstructor // ReSharper disable MemberCanBeMadeStatic.Local -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using MQTTnet.AspNetCore; +//using MQTTnet.AspNetCore; using MQTTnet.Server; namespace MQTTnet.Samples.Server; @@ -20,6 +17,10 @@ namespace MQTTnet.Samples.Server; public static class Server_Hosting_Extensions_Samples { + // This could be called as a top-level statement in a Program.cs file + public static Task Start_Single_Line_Server() + => new HostBuilder().UseMqttServer().Build().RunAsync(); + public static Task Start_Simple_Server() { var host = new HostBuilder() From 10fbb2c704184790706fa20b5c84d5e6446d9850 Mon Sep 17 00:00:00 2001 From: Jeff Scherrer Date: Fri, 20 Jan 2023 18:41:26 -0800 Subject: [PATCH 05/16] Fixed solution changes and a whitespace mistake. --- MQTTnet.sln | 16 +++++++++------- Samples/Server/Server_Asp_Net_Samples.cs | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/MQTTnet.sln b/MQTTnet.sln index a8a0dc104..f97aeb530 100644 --- a/MQTTnet.sln +++ b/MQTTnet.sln @@ -7,9 +7,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\M EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}" ProjectSection(SolutionItems) = preProject - CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md LICENSE = LICENSE README.md = README.md + CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}" @@ -20,17 +20,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedC EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{2BD01D53-4CA5-4142-BE8D-313876395E3E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Samples", "Samples\MQTTnet.Samples.csproj", "{71CF35F5-3327-4A91-AAF4-5340F6701771}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Samples", "Samples\MQTTnet.Samples.csproj", "{71CF35F5-3327-4A91-AAF4-5340F6701771}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Source\MQTTnet.Tests\MQTTnet.Tests.csproj", "{B270F32A-9F3E-42EE-A989-813E35E29ADB}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Tests", "Source\MQTTnet.Tests\MQTTnet.Tests.csproj", "{B270F32A-9F3E-42EE-A989-813E35E29ADB}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Source\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{A238BBBF-C75F-482D-9CC3-BB34ABA9B675}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspNetCore.Tests", "Source\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{A238BBBF-C75F-482D-9CC3-BB34ABA9B675}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Source\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Benchmarks", "Source\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp", "Source\MQTTnet.TestApp\MQTTnet.TestApp.csproj", "{175D5340-CC5B-4542-939D-4E7D15A0BC8D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp", "Source\MQTTnet.TestApp\MQTTnet.TestApp.csproj", "{175D5340-CC5B-4542-939D-4E7D15A0BC8D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspTestApp", "Source\MQTTnet.AspTestApp\MQTTnet.AspTestApp.csproj", "{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspTestApp", "Source\MQTTnet.AspTestApp\MQTTnet.AspTestApp.csproj", "{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Hosting", "Source\MQTTnet.Hosting\MQTTnet.Hosting.csproj", "{B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}" EndProject @@ -92,6 +92,8 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894} EndGlobalSection diff --git a/Samples/Server/Server_Asp_Net_Samples.cs b/Samples/Server/Server_Asp_Net_Samples.cs index 24e4244d1..d085196d4 100644 --- a/Samples/Server/Server_Asp_Net_Samples.cs +++ b/Samples/Server/Server_Asp_Net_Samples.cs @@ -87,7 +87,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment environment, /* * Attach event handlers etc. if required. */ - + server.ValidatingConnectionAsync += mqttController.ValidateConnection; server.ClientConnectedAsync += mqttController.OnClientConnected; }); From 0f22d8b6885c7f16278b294de55ff4fed8695191 Mon Sep 17 00:00:00 2001 From: Jeff Scherrer Date: Sun, 12 Mar 2023 13:54:07 -0700 Subject: [PATCH 06/16] Renamed to extensions hosting and added support for websockets. --- MQTTnet.sln | 4 +- ...tpWebSocketClientAuthenticationCallback.cs | 10 + .../Extensions/HostBuilderExtensions.cs | 61 ++++ .../Extensions/HostingMqttServerExtensions.cs | 25 ++ .../MqttServerWebSocketConnectionHandler.cs | 75 +++++ .../MqttWebSocketServerAdapter.cs | 83 +++++ .../MqttWebSocketServerListener.cs | 119 +++++++ .../MQTTnet.Extensions.Hosting.csproj} | 0 .../MqttHostedServer.cs | 10 +- .../MqttServerConfigurationHostedService.cs | 16 +- .../MqttServerHostingBuilder.cs | 318 ++++++++++++++++++ .../Options/MqttServerHostingOptions.cs | 23 ++ .../MqttServerTlsWebSocketEndpointOptions.cs | 16 + .../MqttServerWebSocketEndpointBaseOptions.cs | 20 ++ .../MqttServerWebSocketEndpointOptions.cs | 16 + .../MQTTnet.Hosting/HostBuilderExtensions.cs | 50 --- .../MqttServerHostingBuilder.cs | 138 -------- Source/MQTTnet.Tests/MQTTnet.Tests.csproj | 2 +- Source/MQTTnet.Tests/Server/Hosting_Tests.cs | 103 +++++- 19 files changed, 883 insertions(+), 206 deletions(-) create mode 100644 Source/MQTTnet.Extensions.Hosting/Events/HttpWebSocketClientAuthenticationCallback.cs create mode 100644 Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs create mode 100644 Source/MQTTnet.Extensions.Hosting/Extensions/HostingMqttServerExtensions.cs create mode 100644 Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs create mode 100644 Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs create mode 100644 Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerListener.cs rename Source/{MQTTnet.Hosting/MQTTnet.Hosting.csproj => MQTTnet.Extensions.Hosting/MQTTnet.Extensions.Hosting.csproj} (100%) rename Source/{MQTTnet.Hosting => MQTTnet.Extensions.Hosting}/MqttHostedServer.cs (63%) rename Source/{MQTTnet.Hosting => MQTTnet.Extensions.Hosting}/MqttServerConfigurationHostedService.cs (61%) create mode 100644 Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs create mode 100644 Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs create mode 100644 Source/MQTTnet.Extensions.Hosting/Options/MqttServerTlsWebSocketEndpointOptions.cs create mode 100644 Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointBaseOptions.cs create mode 100644 Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointOptions.cs delete mode 100644 Source/MQTTnet.Hosting/HostBuilderExtensions.cs delete mode 100644 Source/MQTTnet.Hosting/MqttServerHostingBuilder.cs diff --git a/MQTTnet.sln b/MQTTnet.sln index f97aeb530..e8739565c 100644 --- a/MQTTnet.sln +++ b/MQTTnet.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31919.166 @@ -32,7 +32,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp", "Source\M EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspTestApp", "Source\MQTTnet.AspTestApp\MQTTnet.AspTestApp.csproj", "{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Hosting", "Source\MQTTnet.Hosting\MQTTnet.Hosting.csproj", "{B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Hosting", "Source\MQTTnet.Extensions.Hosting\MQTTnet.Extensions.Hosting.csproj", "{B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Source/MQTTnet.Extensions.Hosting/Events/HttpWebSocketClientAuthenticationCallback.cs b/Source/MQTTnet.Extensions.Hosting/Events/HttpWebSocketClientAuthenticationCallback.cs new file mode 100644 index 000000000..108fdee27 --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/Events/HttpWebSocketClientAuthenticationCallback.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace MQTTnet.Extensions.Hosting.Events +{ + public delegate Task HttpWebSocketClientAuthenticationCallback(); + +} diff --git a/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs b/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs new file mode 100644 index 000000000..2baea6ce3 --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs @@ -0,0 +1,61 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using MQTTnet.Adapter; +using MQTTnet.Diagnostics; +using MQTTnet.Extensions.Hosting; +using MQTTnet.Extensions.Hosting.Implementations; +using MQTTnet.Extensions.Hosting.Options; +using MQTTnet.Implementations; +using MQTTnet.Server; +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Hosting +{ + public static class HostBuilderExtensions + { + + public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder) + => hostBuilder.UseMqttServer(builder => + { + builder.WithDefaultEndpoint(); + }); + + + public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder, Action configure) + { + var startActions = new List>(); + var stopActions = new List>(); + hostBuilder.ConfigureServices((context, services) => + { + services.AddSingleton(s => + { + var builder = new MqttServerHostingBuilder(s, startActions, stopActions); + configure(builder); + return builder.Build(); + }); + + var logger = new MqttNetEventLogger(); + + services + .AddSingleton(logger) + .AddSingleton() + .AddSingleton() + .AddSingleton(s => s.GetRequiredService()) + .AddSingleton(s => new MqttServerConfigurationHostedService(s, startActions, stopActions)) + .AddSingleton(s => s.GetRequiredService()) + + .AddSingleton() + .AddSingleton(s => s.GetRequiredService()) + + .AddSingleton() + + .AddSingleton() + .AddSingleton(s => s.GetRequiredService()); + + }); + return hostBuilder; + } + + } +} diff --git a/Source/MQTTnet.Extensions.Hosting/Extensions/HostingMqttServerExtensions.cs b/Source/MQTTnet.Extensions.Hosting/Extensions/HostingMqttServerExtensions.cs new file mode 100644 index 000000000..a9dd2fd43 --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/Extensions/HostingMqttServerExtensions.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using MQTTnet.Extensions.Hosting; +using MQTTnet.Extensions.Hosting.Implementations; +using MQTTnet.Server; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.WebSockets; +using System.Text; + +namespace MQTTnet.Server +{ + public static class HostingMqttServerExtensions + { + + public static void HandleWebSocketConnection(this MqttServer server, HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext) + { + if (!(server is MqttHostedServer mqttHostedServer)) + throw new InvalidOperationException("The server must be started through hosting extensions."); + + mqttHostedServer.ServiceProvider.GetRequiredService().HandleWebSocketConnection(webSocketContext, httpListenerContext); + } + + } +} diff --git a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs new file mode 100644 index 000000000..798528bb2 --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs @@ -0,0 +1,75 @@ +using Microsoft.Extensions.Hosting; +using MQTTnet.Adapter; +using MQTTnet.Diagnostics; +using MQTTnet.Formatter; +using MQTTnet.Implementations; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.WebSockets; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace MQTTnet.Extensions.Hosting.Implementations +{ + public class MqttServerWebSocketConnectionHandler : IHostedService, IDisposable + { + readonly CancellationTokenSource _cts = new CancellationTokenSource(); + readonly MqttWebSocketServerAdapter _adapter; + readonly IMqttNetLogger _logger; + + public MqttServerWebSocketConnectionHandler(MqttWebSocketServerAdapter adapter, IMqttNetLogger logger) + { + _adapter = adapter; + _logger = logger; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _cts.Cancel(); + + return Task.CompletedTask; + } + + public void HandleWebSocketConnection(HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext, X509Certificate2 clientCertificate = null) + { + _ = Task.Factory.StartNew(() => TryHandleWebSocketConnectionAsync(webSocketContext, httpListenerContext, clientCertificate)); + } + + async Task TryHandleWebSocketConnectionAsync(HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext, X509Certificate2 clientCertificate) + { + if (webSocketContext == null) throw new ArgumentNullException(nameof(webSocketContext)); + var endpoint = $"{httpListenerContext.Request.RemoteEndPoint.Address}:{httpListenerContext.Request.RemoteEndPoint.Port}"; + + try + { + var clientHandler = _adapter.ClientHandler; + if (clientHandler != null) + { + var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)); + var channel = new MqttWebSocketChannel(webSocketContext.WebSocket, endpoint, webSocketContext.IsSecureConnection, clientCertificate); + using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _logger)) + { + await clientHandler(channelAdapter).ConfigureAwait(false); + } + } + } + finally + { + clientCertificate?.Dispose(); + } + } + + public void Dispose() + { + _cts.Dispose(); + } + } +} diff --git a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs new file mode 100644 index 000000000..386e34785 --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs @@ -0,0 +1,83 @@ +using MQTTnet.Adapter; +using MQTTnet.Diagnostics; +using MQTTnet.Internal; +using MQTTnet.Server; +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Net.WebSockets; +using System.Net.Http; +using MQTTnet.Formatter; +using MQTTnet.Extensions.Hosting; +using MQTTnet.Extensions.Hosting.Options; +using MQTTnet.Extensions.Hosting.Implementations; +using Microsoft.Extensions.DependencyInjection; + +namespace MQTTnet.Implementations +{ + public class MqttWebSocketServerAdapter : IMqttServerAdapter + { + readonly List _listeners = new List(); + readonly IServiceProvider _services; + readonly MqttServerHostingOptions _hostingOptions; + MqttServerOptions _serverOptions; + IMqttNetLogger _logger; + + public MqttWebSocketServerAdapter(IServiceProvider services, MqttServerHostingOptions hostingOptions) + { + _services = services; + _hostingOptions = hostingOptions; + } + + public Func ClientHandler { get; set; } + + public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger) + { + _serverOptions = options; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + if (_hostingOptions.DefaultWebSocketEndpointOptions.IsEnabled) + { + _listeners.Add(ActivatorUtilities.CreateInstance(_services, options, _hostingOptions.DefaultWebSocketEndpointOptions)); + } + + if (_hostingOptions.DefaultTlsWebSocketEndpointOptions.IsEnabled) + { + _listeners.Add(ActivatorUtilities.CreateInstance(_services, options, _hostingOptions.DefaultTlsWebSocketEndpointOptions)); + } + + foreach (var listener in _listeners) + { + listener.Start(CancellationToken.None); + } + + return CompletedTask.Instance; + } + + public Task StopAsync() + { + foreach (var listener in _listeners) + { + listener.Dispose(); + } + + _listeners.Clear(); + + return CompletedTask.Instance; + } + + public void Dispose() + { + foreach (var listener in _listeners) + { + listener.Dispose(); + } + } + + } +} diff --git a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerListener.cs b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerListener.cs new file mode 100644 index 000000000..fd715ebb4 --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerListener.cs @@ -0,0 +1,119 @@ +using Microsoft.Extensions.DependencyInjection; +using MQTTnet.Extensions.Hosting.Implementations; +using MQTTnet.Server; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Security; +using System.Net.WebSockets; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace MQTTnet.Implementations +{ + public class MqttWebSocketServerListener : IDisposable + { + readonly IServiceProvider _serviceProvider; + readonly MqttServerOptions _serverOptions; + readonly MqttServerWebSocketEndpointBaseOptions _endpointOptions; + readonly MqttServerWebSocketConnectionHandler _connectionHandler; + private HttpListener _listener; + + public MqttWebSocketServerListener( + IServiceProvider serviceProvider, + MqttServerOptions serverOptions, + MqttServerWebSocketEndpointBaseOptions endpointOptions, + MqttServerWebSocketConnectionHandler connectionHandler) + { + _serviceProvider = serviceProvider; + _serverOptions = serverOptions; + _endpointOptions = endpointOptions; + _connectionHandler = connectionHandler; + } + + public bool Start(CancellationToken cancellationToken) + { + try + { + _listener = new HttpListener(); + + if (_endpointOptions is MqttServerTlsWebSocketEndpointOptions tlsEndpointOptions) + { + if (tlsEndpointOptions.BoundInterNetworkAddress != null && tlsEndpointOptions.BoundInterNetworkAddress != IPAddress.Any) + _listener.Prefixes.Add($"https://{tlsEndpointOptions.BoundInterNetworkAddress}:{tlsEndpointOptions.Port}/"); + if (tlsEndpointOptions.BoundInterNetworkV6Address != null && tlsEndpointOptions.BoundInterNetworkV6Address != IPAddress.IPv6Any) + _listener.Prefixes.Add($"https://{tlsEndpointOptions.BoundInterNetworkV6Address}:{tlsEndpointOptions.Port}/"); + if ((tlsEndpointOptions.BoundInterNetworkAddress == null || + tlsEndpointOptions.BoundInterNetworkAddress == IPAddress.Any) && + (tlsEndpointOptions.BoundInterNetworkV6Address == null || + tlsEndpointOptions.BoundInterNetworkV6Address == IPAddress.IPv6Any)) + _listener.Prefixes.Add($"https://*:{tlsEndpointOptions.Port}/"); + } + else if (_endpointOptions is MqttServerWebSocketEndpointOptions defaultEndpointOptions) + { + if (defaultEndpointOptions.BoundInterNetworkAddress != null && defaultEndpointOptions.BoundInterNetworkAddress != IPAddress.Any) + _listener.Prefixes.Add($"http://{defaultEndpointOptions.BoundInterNetworkAddress}:{defaultEndpointOptions.Port}/"); + if (defaultEndpointOptions.BoundInterNetworkV6Address != null && defaultEndpointOptions.BoundInterNetworkV6Address != IPAddress.IPv6Any) + _listener.Prefixes.Add($"http://{defaultEndpointOptions.BoundInterNetworkV6Address}:{defaultEndpointOptions.Port}/"); + if ((defaultEndpointOptions.BoundInterNetworkAddress == null || + defaultEndpointOptions.BoundInterNetworkAddress == IPAddress.Any) && + (defaultEndpointOptions.BoundInterNetworkV6Address == null || + defaultEndpointOptions.BoundInterNetworkV6Address == IPAddress.IPv6Any)) + _listener.Prefixes.Add($"http://127.0.0.1:{defaultEndpointOptions.Port}/"); // TODO: Correct this to proper wildcard + } + + _listener.Start(); + + Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken); + + return true; + } + catch + { + return false; + } + } + + async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + var context = await _listener.GetContextAsync(); + if (_serverOptions.TlsEndpointOptions.ClientCertificateRequired) + { + var clientCertificate = await context.Request.GetClientCertificateAsync().ConfigureAwait(false); + using var chain = X509Chain.Create(); + if (!_serverOptions.TlsEndpointOptions.RemoteCertificateValidationCallback(this, clientCertificate, chain, SslPolicyErrors.None)) + { + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + context.Response.Close(); + + continue; + } + } + + if (!context.Request.IsWebSocketRequest) + { + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + context.Response.Close(); + + continue; + } + + var webSocketContext = await context.AcceptWebSocketAsync("MQTT", _serverOptions.WriterBufferSize, _serverOptions.KeepAliveMonitorInterval); + + _connectionHandler.HandleWebSocketConnection(webSocketContext, context); + } + } + + + public void Dispose() + { + _listener?.Stop(); + _listener?.Close(); + } + + } +} diff --git a/Source/MQTTnet.Hosting/MQTTnet.Hosting.csproj b/Source/MQTTnet.Extensions.Hosting/MQTTnet.Extensions.Hosting.csproj similarity index 100% rename from Source/MQTTnet.Hosting/MQTTnet.Hosting.csproj rename to Source/MQTTnet.Extensions.Hosting/MQTTnet.Extensions.Hosting.csproj diff --git a/Source/MQTTnet.Hosting/MqttHostedServer.cs b/Source/MQTTnet.Extensions.Hosting/MqttHostedServer.cs similarity index 63% rename from Source/MQTTnet.Hosting/MqttHostedServer.cs rename to Source/MQTTnet.Extensions.Hosting/MqttHostedServer.cs index 3a0eec591..724e074c6 100644 --- a/Source/MQTTnet.Hosting/MqttHostedServer.cs +++ b/Source/MQTTnet.Extensions.Hosting/MqttHostedServer.cs @@ -8,19 +8,21 @@ using MQTTnet.Adapter; using MQTTnet.Diagnostics; -namespace MQTTnet.Hosting +namespace MQTTnet.Extensions.Hosting { public sealed class MqttHostedServer : MqttServer, IHostedService { - public MqttHostedServer(MqttServerOptions options, IEnumerable adapters, IMqttNetLogger logger) + public MqttHostedServer(IServiceProvider serviceProvider, MqttServerOptions options, IEnumerable adapters, IMqttNetLogger logger) : base(options, adapters, logger) { + ServiceProvider = serviceProvider; } + public IServiceProvider ServiceProvider { get; } + public Task StartAsync(CancellationToken cancellationToken) { - _ = StartAsync(); - return Task.CompletedTask; + return StartAsync(); } public Task StopAsync(CancellationToken cancellationToken) diff --git a/Source/MQTTnet.Hosting/MqttServerConfigurationHostedService.cs b/Source/MQTTnet.Extensions.Hosting/MqttServerConfigurationHostedService.cs similarity index 61% rename from Source/MQTTnet.Hosting/MqttServerConfigurationHostedService.cs rename to Source/MQTTnet.Extensions.Hosting/MqttServerConfigurationHostedService.cs index 9ebe65d9c..f45e30391 100644 --- a/Source/MQTTnet.Hosting/MqttServerConfigurationHostedService.cs +++ b/Source/MQTTnet.Extensions.Hosting/MqttServerConfigurationHostedService.cs @@ -7,30 +7,34 @@ using System.Threading; using System.Threading.Tasks; -namespace MQTTnet.Hosting +namespace MQTTnet.Extensions.Hosting { public class MqttServerConfigurationHostedService : IHostedService { private readonly IServiceProvider _serviceProvider; - private readonly List> _configureActions; + private readonly List> _startActions; + private readonly List> _stopActions; - public MqttServerConfigurationHostedService(IServiceProvider serviceProvider, List> configureActions) + public MqttServerConfigurationHostedService(IServiceProvider serviceProvider, List> startActions, List> stopActions) { _serviceProvider = serviceProvider; - _configureActions = configureActions; + _startActions = startActions; + _stopActions = stopActions; } public Task StartAsync(CancellationToken cancellationToken) { var server = _serviceProvider.GetRequiredService(); - _configureActions.ForEach(a => a(server)); + _startActions.ForEach(a => a(server)); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { - + var server = _serviceProvider.GetRequiredService(); + _stopActions.ForEach(a => a(server)); + return Task.CompletedTask; } } diff --git a/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs b/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs new file mode 100644 index 000000000..a23794043 --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs @@ -0,0 +1,318 @@ +using Microsoft.Extensions.DependencyInjection; +using MQTTnet.Extensions.Hosting.Events; +using MQTTnet.Extensions.Hosting.Options; +using MQTTnet.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace MQTTnet.Extensions.Hosting +{ + public class MqttServerHostingBuilder : MqttServerOptionsBuilder + { + readonly MqttServerHostingOptions _hostingOptions; + + readonly List> _startActions; + readonly List> _stopActions; + + public MqttServerHostingBuilder(IServiceProvider serviceProvider, List> startActions, List> stopActions) + { + ServiceProvider = serviceProvider; + _hostingOptions = serviceProvider.GetRequiredService(); + _startActions = startActions; + _stopActions = stopActions; + } + + public event Func ApplicationMessageNotConsumedAsync + { + add + { + _startActions.Add(server => server.ApplicationMessageNotConsumedAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.ApplicationMessageNotConsumedAsync -= value); + } + remove => _startActions.Add(server => server.ApplicationMessageNotConsumedAsync -= value); + } + + public event Func ClientAcknowledgedPublishPacketAsync + { + add + { + _startActions.Add(server => server.ClientAcknowledgedPublishPacketAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.ClientAcknowledgedPublishPacketAsync -= value); + } + remove => _startActions.Add(server => server.ClientAcknowledgedPublishPacketAsync -= value); + } + + public event Func ClientConnectedAsync + { + add + { + _startActions.Add(server => server.ClientConnectedAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.ClientConnectedAsync -= value); + } + remove => _startActions.Add(server => server.ClientConnectedAsync -= value); + } + + public event Func ClientDisconnectedAsync + { + add + { + _startActions.Add(server => server.ClientDisconnectedAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.ClientDisconnectedAsync -= value); + } + remove => _startActions.Add(server => server.ClientDisconnectedAsync -= value); + } + + public event Func ClientSubscribedTopicAsync + { + add + { + _startActions.Add(server => server.ClientSubscribedTopicAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.ClientSubscribedTopicAsync -= value); + } + remove => _startActions.Add(server => server.ClientSubscribedTopicAsync -= value); + } + + public event Func ClientUnsubscribedTopicAsync + { + add + { + _startActions.Add(server => server.ClientUnsubscribedTopicAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.ClientUnsubscribedTopicAsync -= value); + } + remove => _startActions.Add(server => server.ClientUnsubscribedTopicAsync -= value); + } + + public event Func InterceptingInboundPacketAsync + { + add + { + _startActions.Add(server => server.InterceptingInboundPacketAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.InterceptingInboundPacketAsync -= value); + } + remove => _startActions.Add(server => server.InterceptingInboundPacketAsync -= value); + } + + public event Func InterceptingOutboundPacketAsync + { + add + { + _startActions.Add(server => server.InterceptingOutboundPacketAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.InterceptingOutboundPacketAsync -= value); + } + remove => _startActions.Add(server => server.InterceptingOutboundPacketAsync -= value); + } + + public event Func InterceptingPublishAsync + { + add + { + _startActions.Add(server => server.InterceptingPublishAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.InterceptingPublishAsync -= value); + } + remove => _startActions.Add(server => server.InterceptingPublishAsync -= value); + } + + public event Func InterceptingSubscriptionAsync + { + add + { + _startActions.Add(server => server.InterceptingSubscriptionAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.InterceptingSubscriptionAsync -= value); + } + remove => _startActions.Add(server => server.InterceptingSubscriptionAsync -= value); + } + + public event Func InterceptingUnsubscriptionAsync + { + add + { + _startActions.Add(server => server.InterceptingUnsubscriptionAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.InterceptingUnsubscriptionAsync -= value); + } + remove => _startActions.Add(server => server.InterceptingUnsubscriptionAsync -= value); + } + + public event Func LoadingRetainedMessageAsync + { + add + { + _startActions.Add(server => server.LoadingRetainedMessageAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.LoadingRetainedMessageAsync -= value); + } + remove => _startActions.Add(server => server.LoadingRetainedMessageAsync -= value); + } + + public event Func PreparingSessionAsync + { + add + { + _startActions.Add(server => server.PreparingSessionAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.PreparingSessionAsync -= value); + } + remove => _startActions.Add(server => server.PreparingSessionAsync -= value); + } + + public event Func RetainedMessageChangedAsync + { + add + { + _startActions.Add(server => server.RetainedMessageChangedAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.RetainedMessageChangedAsync -= value); + } + remove => _startActions.Add(server => server.RetainedMessageChangedAsync -= value); + } + + public event Func RetainedMessagesClearedAsync + { + add + { + _startActions.Add(server => server.RetainedMessagesClearedAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.RetainedMessagesClearedAsync -= value); + } + remove => _startActions.Add(server => server.RetainedMessagesClearedAsync -= value); + } + + public event Func SessionDeletedAsync + { + add + { + _startActions.Add(server => server.SessionDeletedAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.SessionDeletedAsync -= value); + } + remove => _startActions.Add(server => server.SessionDeletedAsync -= value); + } + + public event Func StartedAsync + { + add + { + _startActions.Add(server => server.StartedAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.StartedAsync -= value); + } + remove => _startActions.Add(server => server.StartedAsync -= value); + } + + public event Func StoppedAsync + { + add + { + _startActions.Add(server => server.StoppedAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.StoppedAsync -= value); + } + remove => _startActions.Add(server => server.StoppedAsync -= value); + } + + public event Func ValidatingConnectionAsync + { + add + { + _startActions.Add(server => server.ValidatingConnectionAsync += value); + if (_hostingOptions.AutoRemoveEventHandlers) + _stopActions.Add(server => server.ValidatingConnectionAsync -= value); + } + remove => _startActions.Add(server => server.ValidatingConnectionAsync -= value); + } + + public IServiceProvider ServiceProvider { get; } + + public MqttServerHostingBuilder WithDefaultWebSocketEndpoint() + { + _hostingOptions.DefaultWebSocketEndpointOptions.IsEnabled = true; + + return this; + } + + public MqttServerHostingBuilder WithoutDefaultWebSocketEndpoint() + { + _hostingOptions.DefaultWebSocketEndpointOptions.IsEnabled = false; + + return this; + } + + public MqttServerHostingBuilder WithDefaultWebSocketEndpointPort(int value) + { + _hostingOptions.DefaultWebSocketEndpointOptions.Port = value; + + return this; + } + + public MqttServerHostingBuilder WithDefaultWebSocketEndpointBoundIPAddress(IPAddress value) + { + _hostingOptions.DefaultWebSocketEndpointOptions.BoundInterNetworkAddress = value; + + return this; + } + + public MqttServerHostingBuilder WithDefaultWebSocketEndpointBoundIPV6Address(IPAddress value) + { + _hostingOptions.DefaultWebSocketEndpointOptions.BoundInterNetworkV6Address = value; + + return this; + } + + public MqttServerHostingBuilder WithEncryptedWebSocketEndpoint() + { + _hostingOptions.DefaultTlsWebSocketEndpointOptions.IsEnabled = true; + + return this; + } + + public MqttServerHostingBuilder WithEncryptedWebSocketEndpointPort(int value) + { + _hostingOptions.DefaultTlsWebSocketEndpointOptions.Port = value; + + return this; + } + + public MqttServerHostingBuilder WithEncryptedWebSocketEndpointBoundIPAddress(IPAddress value) + { + _hostingOptions.DefaultTlsWebSocketEndpointOptions.BoundInterNetworkAddress = value; + + return this; + } + + public MqttServerHostingBuilder WithEncryptedWebSocketEndpointBoundIPV6Address(IPAddress value) + { + _hostingOptions.DefaultTlsWebSocketEndpointOptions.BoundInterNetworkV6Address = value; + + return this; + } + + public MqttServerHostingBuilder WithWebSocketRoute(string value) + { + _hostingOptions.WebSocketRoute = value; + + return this; + } + + public MqttServerHostingBuilder WithWebSocketClientAuthentication(HttpWebSocketClientAuthenticationCallback callback) + { + _hostingOptions.WebSocketAuthenticationCallback = callback; + + return this; + } + + } +} diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs new file mode 100644 index 000000000..d712a62df --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs @@ -0,0 +1,23 @@ +using MQTTnet.Extensions.Hosting.Events; +using MQTTnet.Server; +using System; +using System.Collections.Generic; +using System.Text; + +namespace MQTTnet.Extensions.Hosting.Options +{ + public class MqttServerHostingOptions + { + + public bool AutoRemoveEventHandlers { get; set; } = true; + + public MqttServerWebSocketEndpointOptions DefaultWebSocketEndpointOptions { get; } = new MqttServerWebSocketEndpointOptions(); + + public MqttServerTlsWebSocketEndpointOptions DefaultTlsWebSocketEndpointOptions { get; } = new MqttServerTlsWebSocketEndpointOptions(); + + public HttpWebSocketClientAuthenticationCallback WebSocketAuthenticationCallback { get; set; } + + public string WebSocketRoute { get; set; } + + } +} diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerTlsWebSocketEndpointOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerTlsWebSocketEndpointOptions.cs new file mode 100644 index 000000000..b1bf6508e --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerTlsWebSocketEndpointOptions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MQTTnet.Server +{ + public class MqttServerTlsWebSocketEndpointOptions : MqttServerWebSocketEndpointBaseOptions + { + + public MqttServerTlsWebSocketEndpointOptions() + { + Port = 443; + } + + } +} diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointBaseOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointBaseOptions.cs new file mode 100644 index 000000000..461d30196 --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointBaseOptions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace MQTTnet.Server +{ + public abstract class MqttServerWebSocketEndpointBaseOptions + { + + public bool IsEnabled { get; set; } + + public int Port { get; set; } + + public IPAddress BoundInterNetworkAddress { get; set; } = IPAddress.Any; + + public IPAddress BoundInterNetworkV6Address { get; set; } = IPAddress.IPv6Any; + + } +} diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointOptions.cs new file mode 100644 index 000000000..47d115899 --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointOptions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MQTTnet.Server +{ + public class MqttServerWebSocketEndpointOptions : MqttServerWebSocketEndpointBaseOptions + { + + public MqttServerWebSocketEndpointOptions() + { + Port = 80; + } + + } +} diff --git a/Source/MQTTnet.Hosting/HostBuilderExtensions.cs b/Source/MQTTnet.Hosting/HostBuilderExtensions.cs deleted file mode 100644 index 498688751..000000000 --- a/Source/MQTTnet.Hosting/HostBuilderExtensions.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using MQTTnet.Adapter; -using MQTTnet.Diagnostics; -using MQTTnet.Hosting; -using MQTTnet.Implementations; -using MQTTnet.Server; -using System; -using System.Collections.Generic; - -namespace Microsoft.Extensions.Hosting -{ - public static class HostBuilderExtensions - { - - public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder) - => UseMqttServer(hostBuilder, builder => - { - builder.WithDefaultEndpoint(); - }); - - - public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder, Action configure) - { - var configureActions = new List>(); - hostBuilder.ConfigureServices((context, services) => - { - services.AddSingleton(s => - { - var builder = new MqttServerHostingBuilder(s, configureActions); - configure(builder); - return builder.Build(); - }); - - var logger = new MqttNetEventLogger(); - - services.AddSingleton(logger); - services.AddSingleton(); - services.AddSingleton(s => s.GetRequiredService()); - services.AddSingleton(s => new MqttServerConfigurationHostedService(s, configureActions)); - services.AddSingleton(s => s.GetRequiredService()); - - services.AddSingleton(); - services.AddSingleton(s => s.GetService()); - - }); - return hostBuilder; - } - - } -} diff --git a/Source/MQTTnet.Hosting/MqttServerHostingBuilder.cs b/Source/MQTTnet.Hosting/MqttServerHostingBuilder.cs deleted file mode 100644 index 8b671a38b..000000000 --- a/Source/MQTTnet.Hosting/MqttServerHostingBuilder.cs +++ /dev/null @@ -1,138 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using MQTTnet.Server; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MQTTnet.Hosting -{ - public class MqttServerHostingBuilder : MqttServerOptionsBuilder - { - private readonly List> _configureActions; - - public MqttServerHostingBuilder(IServiceProvider serviceProvider, List> configureActions) - { - ServiceProvider = serviceProvider; - _configureActions = configureActions; - } - - public event Func ApplicationMessageNotConsumedAsync - { - add => _configureActions.Add(server => server.ApplicationMessageNotConsumedAsync += value); - remove => _configureActions.Add(server => server.ApplicationMessageNotConsumedAsync -= value); - } - - public event Func ClientAcknowledgedPublishPacketAsync - { - add => _configureActions.Add(server => server.ClientAcknowledgedPublishPacketAsync += value); - remove => _configureActions.Add(server => server.ClientAcknowledgedPublishPacketAsync -= value); - } - - public event Func ClientConnectedAsync - { - add => _configureActions.Add(server => server.ClientConnectedAsync += value); - remove => _configureActions.Add(server => server.ClientConnectedAsync -= value); - } - - public event Func ClientDisconnectedAsync - { - add => _configureActions.Add(server => server.ClientDisconnectedAsync += value); - remove => _configureActions.Add(server => server.ClientDisconnectedAsync -= value); - } - - public event Func ClientSubscribedTopicAsync - { - add => _configureActions.Add(server => server.ClientSubscribedTopicAsync += value); - remove => _configureActions.Add(server => server.ClientSubscribedTopicAsync -= value); - } - - public event Func ClientUnsubscribedTopicAsync - { - add => _configureActions.Add(server => server.ClientUnsubscribedTopicAsync += value); - remove => _configureActions.Add(server => server.ClientUnsubscribedTopicAsync -= value); - } - - public event Func InterceptingInboundPacketAsync - { - add => _configureActions.Add(server => server.InterceptingInboundPacketAsync += value); - remove => _configureActions.Add(server => server.InterceptingInboundPacketAsync -= value); - } - - public event Func InterceptingOutboundPacketAsync - { - add => _configureActions.Add(server => server.InterceptingOutboundPacketAsync += value); - remove => _configureActions.Add(server => server.InterceptingOutboundPacketAsync -= value); - } - - public event Func InterceptingPublishAsync - { - add => _configureActions.Add(server => server.InterceptingPublishAsync += value); - remove => _configureActions.Add(server => server.InterceptingPublishAsync -= value); - } - - public event Func InterceptingSubscriptionAsync - { - add => _configureActions.Add(server => server.InterceptingSubscriptionAsync += value); - remove => _configureActions.Add(server => server.InterceptingSubscriptionAsync -= value); - } - - public event Func InterceptingUnsubscriptionAsync - { - add => _configureActions.Add(server => server.InterceptingUnsubscriptionAsync += value); - remove => _configureActions.Add(server => server.InterceptingUnsubscriptionAsync -= value); - } - - public event Func LoadingRetainedMessageAsync - { - add => _configureActions.Add(server => server.LoadingRetainedMessageAsync += value); - remove => _configureActions.Add(server => server.LoadingRetainedMessageAsync -= value); - } - - public event Func PreparingSessionAsync - { - add => _configureActions.Add(server => server.PreparingSessionAsync += value); - remove => _configureActions.Add(server => server.PreparingSessionAsync -= value); - } - - public event Func RetainedMessageChangedAsync - { - add => _configureActions.Add(server => server.RetainedMessageChangedAsync += value); - remove => _configureActions.Add(server => server.RetainedMessageChangedAsync -= value); - } - - public event Func RetainedMessagesClearedAsync - { - add => _configureActions.Add(server => server.RetainedMessagesClearedAsync += value); - remove => _configureActions.Add(server => server.RetainedMessagesClearedAsync -= value); - } - - public event Func SessionDeletedAsync - { - add => _configureActions.Add(server => server.SessionDeletedAsync += value); - remove => _configureActions.Add(server => server.SessionDeletedAsync -= value); - } - - public event Func StartedAsync - { - add => _configureActions.Add(server => server.StartedAsync += value); - remove => _configureActions.Add(server => server.StartedAsync -= value); - } - - public event Func StoppedAsync - { - add => _configureActions.Add(server => server.StoppedAsync += value); - remove => _configureActions.Add(server => server.StoppedAsync -= value); - } - - public event Func ValidatingConnectionAsync - { - add => _configureActions.Add(server => server.ValidatingConnectionAsync += value); - remove => _configureActions.Add(server => server.ValidatingConnectionAsync -= value); - } - - public IServiceProvider ServiceProvider { get; } - - } -} diff --git a/Source/MQTTnet.Tests/MQTTnet.Tests.csproj b/Source/MQTTnet.Tests/MQTTnet.Tests.csproj index 5f508b62a..f757aa92d 100644 --- a/Source/MQTTnet.Tests/MQTTnet.Tests.csproj +++ b/Source/MQTTnet.Tests/MQTTnet.Tests.csproj @@ -21,7 +21,7 @@ - + diff --git a/Source/MQTTnet.Tests/Server/Hosting_Tests.cs b/Source/MQTTnet.Tests/Server/Hosting_Tests.cs index 1edc37e87..0b40ad477 100644 --- a/Source/MQTTnet.Tests/Server/Hosting_Tests.cs +++ b/Source/MQTTnet.Tests/Server/Hosting_Tests.cs @@ -1,13 +1,12 @@ +#if NET5_0_OR_GREATER using Microsoft.VisualStudio.TestTools.UnitTesting; -using MQTTnet.Client; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using System.Threading; -#if NET5_0_OR_GREATER +using System.Net; +using MQTTnet.Server; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; namespace MQTTnet.Tests.Server { @@ -116,6 +115,100 @@ public async Task Advanced_Host_Configuration() Assert.AreEqual(1, connectedClientCount); } + [TestMethod] + public async Task Default_WebSocket_Configuration_Connect() + { + var host = new HostBuilder() + .UseMqttServer(mqtt => + { + mqtt + .WithDefaultWebSocketEndpoint() + .WithDefaultWebSocketEndpointPort(8080); + }) + .Build(); + await host.StartAsync(); + + await Task.Delay(5000); + + // Perform client connect test + try + { + var factory = new MqttFactory(); + var client = factory.CreateMqttClient(); + var options = factory.CreateClientOptionsBuilder(); + options + .WithWebSocketServer("127.0.0.1:8080/mqtt"); + + await client.ConnectAsync(options.Build()); + } + finally + { + await host.StopAsync(); + } + } + + [TestMethod] + public async Task External_HttpListener_WebSocket_Configuration_Connect() + { + using (var tcs = new CancellationTokenSource()) + { + var host = new HostBuilder() + .UseMqttServer() + .Build(); + await host.StartAsync(); + + var httpListener = new HttpListener(); + httpListener.Prefixes.Add("http://127.0.0.1:8080/"); + httpListener.Start(); + + _ = Task.Factory.StartNew(async () => + { + while (!tcs.IsCancellationRequested) + { + try + { + var context = await httpListener.GetContextAsync(); + + if (context.Request.Url.AbsolutePath.Equals("/mqtt", StringComparison.OrdinalIgnoreCase) && context.Request.IsWebSocketRequest) + { + var mqttServer = host.Services.GetService(); + var webSocketContext = await context.AcceptWebSocketAsync("MQTT"); + mqttServer.HandleWebSocketConnection(webSocketContext, context); + } + else + { + context.Response.StatusCode = 404; + context.Response.Close(); + } + } + catch + { + + } + } + }); + + await Task.Delay(5000); + + // Perform client connect test + try + { + var factory = new MqttFactory(); + var client = factory.CreateMqttClient(); + var options = factory.CreateClientOptionsBuilder(); + options + .WithWebSocketServer("127.0.0.1:8080/mqtt"); + + await client.ConnectAsync(options.Build()); + } + finally + { + await host.StopAsync(); + httpListener.Stop(); + } + } + } + } } From 1d0a6ad6e807317e5c8997ee9ce4bb6b705364e3 Mon Sep 17 00:00:00 2001 From: Christian <6939810+chkr1011@users.noreply.github.com> Date: Mon, 1 May 2023 11:56:51 +0200 Subject: [PATCH 07/16] Fix broken project reference --- .github/workflows/ReleaseNotes.md | 1 + Samples/MQTTnet.Samples.csproj | 2 +- .../MqttWebSocketServerAdapter.cs | 9 -- .../MqttServerHostingBuilder.cs | 92 +++++++++++++------ .../Options/MqttServerHostingOptions.cs | 1 - 5 files changed, 65 insertions(+), 40 deletions(-) diff --git a/.github/workflows/ReleaseNotes.md b/.github/workflows/ReleaseNotes.md index d534f1b7f..5b0482734 100644 --- a/.github/workflows/ReleaseNotes.md +++ b/.github/workflows/ReleaseNotes.md @@ -12,3 +12,4 @@ * [Server] Added a new API method which allows reading a single retained message without the need to processing the entire set of retained messages (#1659). * [Server] Added a new event (InterceptingClientEnqueueAsync) which allows intercepting when an application message is enqueued for a client (#1648). * [Server] Fixed race condition when handling connections which leads to stopped message transfers (#1677, thanks to @RazvanEmilR). +* [Server] Added a new extension nuget which allows hosting a MQTT server via the diff --git a/Samples/MQTTnet.Samples.csproj b/Samples/MQTTnet.Samples.csproj index fc61f512b..84eb34654 100644 --- a/Samples/MQTTnet.Samples.csproj +++ b/Samples/MQTTnet.Samples.csproj @@ -18,7 +18,7 @@ - + diff --git a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs index 386e34785..39d193a8c 100644 --- a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs +++ b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs @@ -4,18 +4,9 @@ using MQTTnet.Server; using System; using System.Collections.Generic; -using System.Net.Sockets; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Net.WebSockets; -using System.Net.Http; -using MQTTnet.Formatter; -using MQTTnet.Extensions.Hosting; using MQTTnet.Extensions.Hosting.Options; -using MQTTnet.Extensions.Hosting.Implementations; using Microsoft.Extensions.DependencyInjection; namespace MQTTnet.Implementations diff --git a/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs b/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs index a23794043..63379a208 100644 --- a/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs +++ b/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs @@ -1,20 +1,17 @@ -using Microsoft.Extensions.DependencyInjection; -using MQTTnet.Extensions.Hosting.Events; -using MQTTnet.Extensions.Hosting.Options; -using MQTTnet.Server; using System; using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Text; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using MQTTnet.Extensions.Hosting.Events; +using MQTTnet.Extensions.Hosting.Options; +using MQTTnet.Server; namespace MQTTnet.Extensions.Hosting { public class MqttServerHostingBuilder : MqttServerOptionsBuilder { readonly MqttServerHostingOptions _hostingOptions; - readonly List> _startActions; readonly List> _stopActions; @@ -32,7 +29,9 @@ public event Func ApplicationMessa { _startActions.Add(server => server.ApplicationMessageNotConsumedAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.ApplicationMessageNotConsumedAsync -= value); + } } remove => _startActions.Add(server => server.ApplicationMessageNotConsumedAsync -= value); } @@ -43,7 +42,9 @@ public event Func ClientAcknowle { _startActions.Add(server => server.ClientAcknowledgedPublishPacketAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.ClientAcknowledgedPublishPacketAsync -= value); + } } remove => _startActions.Add(server => server.ClientAcknowledgedPublishPacketAsync -= value); } @@ -54,7 +55,9 @@ public event Func ClientConnectedAsync { _startActions.Add(server => server.ClientConnectedAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.ClientConnectedAsync -= value); + } } remove => _startActions.Add(server => server.ClientConnectedAsync -= value); } @@ -65,7 +68,9 @@ public event Func ClientDisconnectedAsync { _startActions.Add(server => server.ClientDisconnectedAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.ClientDisconnectedAsync -= value); + } } remove => _startActions.Add(server => server.ClientDisconnectedAsync -= value); } @@ -76,7 +81,9 @@ public event Func ClientSubscribedTopicAsy { _startActions.Add(server => server.ClientSubscribedTopicAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.ClientSubscribedTopicAsync -= value); + } } remove => _startActions.Add(server => server.ClientSubscribedTopicAsync -= value); } @@ -87,7 +94,9 @@ public event Func ClientUnsubscribedTopi { _startActions.Add(server => server.ClientUnsubscribedTopicAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.ClientUnsubscribedTopicAsync -= value); + } } remove => _startActions.Add(server => server.ClientUnsubscribedTopicAsync -= value); } @@ -98,7 +107,9 @@ public event Func InterceptingInboundPacketAs { _startActions.Add(server => server.InterceptingInboundPacketAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.InterceptingInboundPacketAsync -= value); + } } remove => _startActions.Add(server => server.InterceptingInboundPacketAsync -= value); } @@ -109,7 +120,9 @@ public event Func InterceptingOutboundPacketA { _startActions.Add(server => server.InterceptingOutboundPacketAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.InterceptingOutboundPacketAsync -= value); + } } remove => _startActions.Add(server => server.InterceptingOutboundPacketAsync -= value); } @@ -120,7 +133,9 @@ public event Func InterceptingPublishAsync { _startActions.Add(server => server.InterceptingPublishAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.InterceptingPublishAsync -= value); + } } remove => _startActions.Add(server => server.InterceptingPublishAsync -= value); } @@ -131,7 +146,9 @@ public event Func InterceptingSubscript { _startActions.Add(server => server.InterceptingSubscriptionAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.InterceptingSubscriptionAsync -= value); + } } remove => _startActions.Add(server => server.InterceptingSubscriptionAsync -= value); } @@ -142,7 +159,9 @@ public event Func InterceptingUnsubsc { _startActions.Add(server => server.InterceptingUnsubscriptionAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.InterceptingUnsubscriptionAsync -= value); + } } remove => _startActions.Add(server => server.InterceptingUnsubscriptionAsync -= value); } @@ -153,7 +172,9 @@ public event Func LoadingRetainedMessage { _startActions.Add(server => server.LoadingRetainedMessageAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.LoadingRetainedMessageAsync -= value); + } } remove => _startActions.Add(server => server.LoadingRetainedMessageAsync -= value); } @@ -164,7 +185,9 @@ public event Func PreparingSessionAsync { _startActions.Add(server => server.PreparingSessionAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.PreparingSessionAsync -= value); + } } remove => _startActions.Add(server => server.PreparingSessionAsync -= value); } @@ -175,7 +198,9 @@ public event Func RetainedMessageChangedA { _startActions.Add(server => server.RetainedMessageChangedAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.RetainedMessageChangedAsync -= value); + } } remove => _startActions.Add(server => server.RetainedMessageChangedAsync -= value); } @@ -186,7 +211,9 @@ public event Func RetainedMessagesClearedAsync { _startActions.Add(server => server.RetainedMessagesClearedAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.RetainedMessagesClearedAsync -= value); + } } remove => _startActions.Add(server => server.RetainedMessagesClearedAsync -= value); } @@ -197,7 +224,9 @@ public event Func SessionDeletedAsync { _startActions.Add(server => server.SessionDeletedAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.SessionDeletedAsync -= value); + } } remove => _startActions.Add(server => server.SessionDeletedAsync -= value); } @@ -208,7 +237,9 @@ public event Func StartedAsync { _startActions.Add(server => server.StartedAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.StartedAsync -= value); + } } remove => _startActions.Add(server => server.StartedAsync -= value); } @@ -219,7 +250,9 @@ public event Func StoppedAsync { _startActions.Add(server => server.StoppedAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.StoppedAsync -= value); + } } remove => _startActions.Add(server => server.StoppedAsync -= value); } @@ -230,7 +263,9 @@ public event Func ValidatingConnectionAsync { _startActions.Add(server => server.ValidatingConnectionAsync += value); if (_hostingOptions.AutoRemoveEventHandlers) + { _stopActions.Add(server => server.ValidatingConnectionAsync -= value); + } } remove => _startActions.Add(server => server.ValidatingConnectionAsync -= value); } @@ -240,28 +275,14 @@ public event Func ValidatingConnectionAsync public MqttServerHostingBuilder WithDefaultWebSocketEndpoint() { _hostingOptions.DefaultWebSocketEndpointOptions.IsEnabled = true; - - return this; - } - - public MqttServerHostingBuilder WithoutDefaultWebSocketEndpoint() - { - _hostingOptions.DefaultWebSocketEndpointOptions.IsEnabled = false; - - return this; - } - public MqttServerHostingBuilder WithDefaultWebSocketEndpointPort(int value) - { - _hostingOptions.DefaultWebSocketEndpointOptions.Port = value; - return this; } public MqttServerHostingBuilder WithDefaultWebSocketEndpointBoundIPAddress(IPAddress value) { _hostingOptions.DefaultWebSocketEndpointOptions.BoundInterNetworkAddress = value; - + return this; } @@ -272,16 +293,16 @@ public MqttServerHostingBuilder WithDefaultWebSocketEndpointBoundIPV6Address(IPA return this; } - public MqttServerHostingBuilder WithEncryptedWebSocketEndpoint() + public MqttServerHostingBuilder WithDefaultWebSocketEndpointPort(int value) { - _hostingOptions.DefaultTlsWebSocketEndpointOptions.IsEnabled = true; + _hostingOptions.DefaultWebSocketEndpointOptions.Port = value; return this; } - public MqttServerHostingBuilder WithEncryptedWebSocketEndpointPort(int value) + public MqttServerHostingBuilder WithEncryptedWebSocketEndpoint() { - _hostingOptions.DefaultTlsWebSocketEndpointOptions.Port = value; + _hostingOptions.DefaultTlsWebSocketEndpointOptions.IsEnabled = true; return this; } @@ -300,9 +321,16 @@ public MqttServerHostingBuilder WithEncryptedWebSocketEndpointBoundIPV6Address(I return this; } - public MqttServerHostingBuilder WithWebSocketRoute(string value) + public MqttServerHostingBuilder WithEncryptedWebSocketEndpointPort(int value) { - _hostingOptions.WebSocketRoute = value; + _hostingOptions.DefaultTlsWebSocketEndpointOptions.Port = value; + + return this; + } + + public MqttServerHostingBuilder WithoutDefaultWebSocketEndpoint() + { + _hostingOptions.DefaultWebSocketEndpointOptions.IsEnabled = false; return this; } @@ -314,5 +342,11 @@ public MqttServerHostingBuilder WithWebSocketClientAuthentication(HttpWebSocketC return this; } + public MqttServerHostingBuilder WithWebSocketRoute(string value) + { + _hostingOptions.WebSocketRoute = value; + + return this; + } } -} +} \ No newline at end of file diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs index d712a62df..f4c6a7bd1 100644 --- a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs @@ -8,7 +8,6 @@ namespace MQTTnet.Extensions.Hosting.Options { public class MqttServerHostingOptions { - public bool AutoRemoveEventHandlers { get; set; } = true; public MqttServerWebSocketEndpointOptions DefaultWebSocketEndpointOptions { get; } = new MqttServerWebSocketEndpointOptions(); From 02f825cf2129bdd78bcf318d22fdccf1cf79d09b Mon Sep 17 00:00:00 2001 From: Jonathan Kallay Date: Wed, 25 Oct 2023 13:53:51 -0700 Subject: [PATCH 08/16] Completes the ReleaseNotes sentence. --- .github/workflows/ReleaseNotes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ReleaseNotes.md b/.github/workflows/ReleaseNotes.md index e70201723..9d37cc4ae 100644 --- a/.github/workflows/ReleaseNotes.md +++ b/.github/workflows/ReleaseNotes.md @@ -1,3 +1,3 @@ * [Client] Fixed wrong TLS options handling (#1830). * [Client] Fixed NullReferenceExeption when performing a Ping when the client is not connected (#1831). -* [Server] Added a new extension nuget which allows hosting a MQTT server via the +* [Server] Added a new extension nuget which allows hosting a MQTT server via the Microsoft.Extensions.Hosting library. From a3bca6abaf5e0a702bea8f98a810c617de3b0c4f Mon Sep 17 00:00:00 2001 From: Jonathan Kallay Date: Wed, 25 Oct 2023 13:59:32 -0700 Subject: [PATCH 09/16] Build error --- .../Implementations/MqttServerWebSocketConnectionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs index 798528bb2..98aadab87 100644 --- a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs +++ b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs @@ -55,7 +55,7 @@ async Task TryHandleWebSocketConnectionAsync(HttpListenerWebSocketContext webSoc { var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)); var channel = new MqttWebSocketChannel(webSocketContext.WebSocket, endpoint, webSocketContext.IsSecureConnection, clientCertificate); - using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _logger)) + using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger)) { await clientHandler(channelAdapter).ConfigureAwait(false); } From 5cad98317220d5a44284ac2bbb4ba94a305d0b25 Mon Sep 17 00:00:00 2001 From: Christian <6939810+chkr1011@users.noreply.github.com> Date: Sat, 18 Nov 2023 10:20:44 +0100 Subject: [PATCH 10/16] Update ReleaseNotes.md --- .github/workflows/ReleaseNotes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ReleaseNotes.md b/.github/workflows/ReleaseNotes.md index 02a34445c..80c9a80bd 100644 --- a/.github/workflows/ReleaseNotes.md +++ b/.github/workflows/ReleaseNotes.md @@ -1,4 +1,4 @@ * [Server] Fixed not working _UpdateRetainedMessageAsync_ public api (#1858, thanks to @kimdiego2098). * [Client] Added support for custom CA chain validation (#1851, thanks to @rido-min). * [Client] Fixed handling of unobserved tasks exceptions (#1871). -* [Server] Added a new extension nuget which allows hosting a MQTT server via the Microsoft.Extensions.Hosting library. \ No newline at end of file +* [Server] Added a new extension nuget which allows hosting a MQTT server via the Microsoft.Extensions.Hosting library (#1653, thanks to @YAJeff). \ No newline at end of file From db37cfbcb737fcc102fad789611eb4164f7296b5 Mon Sep 17 00:00:00 2001 From: Christian <6939810+chkr1011@users.noreply.github.com> Date: Sat, 18 Nov 2023 10:26:16 +0100 Subject: [PATCH 11/16] Apply code style --- .../Server_Hosting_Extensions_Samples.cs | 33 ++-- Source/MQTTnet.Tests/Server/Hosting_Tests.cs | 144 ++++++++---------- 2 files changed, 78 insertions(+), 99 deletions(-) diff --git a/Samples/Server/Server_Hosting_Extensions_Samples.cs b/Samples/Server/Server_Hosting_Extensions_Samples.cs index 840c37751..35633d5f8 100644 --- a/Samples/Server/Server_Hosting_Extensions_Samples.cs +++ b/Samples/Server/Server_Hosting_Extensions_Samples.cs @@ -9,33 +9,17 @@ // ReSharper disable MemberCanBeMadeStatic.Local using Microsoft.Extensions.Hosting; -//using MQTTnet.AspNetCore; -using MQTTnet.Server; namespace MQTTnet.Samples.Server; public static class Server_Hosting_Extensions_Samples { - - // This could be called as a top-level statement in a Program.cs file - public static Task Start_Single_Line_Server() - => new HostBuilder().UseMqttServer().Build().RunAsync(); - - public static Task Start_Simple_Server() - { - var host = new HostBuilder() - .UseMqttServer() - .Build(); - - return host.RunAsync(); - } - public static Task Start_Server() { var builder = new HostBuilder(); - builder - .UseMqttServer(mqtt => + builder.UseMqttServer( + mqtt => { mqtt.WithDefaultEndpoint(); }); @@ -43,4 +27,17 @@ public static Task Start_Server() var host = builder.Build(); return host.RunAsync(); } + + public static Task Start_Simple_Server() + { + var host = new HostBuilder().UseMqttServer().Build(); + + return host.RunAsync(); + } + + // This could be called as a top-level statement in a Program.cs file + public static Task Start_Single_Line_Server() + { + return new HostBuilder().UseMqttServer().Build().RunAsync(); + } } \ No newline at end of file diff --git a/Source/MQTTnet.Tests/Server/Hosting_Tests.cs b/Source/MQTTnet.Tests/Server/Hosting_Tests.cs index 0b40ad477..ead05a0dc 100644 --- a/Source/MQTTnet.Tests/Server/Hosting_Tests.cs +++ b/Source/MQTTnet.Tests/Server/Hosting_Tests.cs @@ -1,27 +1,40 @@ #if NET5_0_OR_GREATER -using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -using System.Threading.Tasks; -using System.Threading; using System.Net; -using MQTTnet.Server; -using Microsoft.Extensions.Hosting; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MQTTnet.Server; namespace MQTTnet.Tests.Server { - [TestClass] - public class Hosting_Tests + public sealed class Hosting_Tests { - [TestMethod] - public async Task Default_Host_Configuration() + public async Task Advanced_Host_Configuration() { + var syncLock = new object(); + var connectedClientCount = 0; + var host = new HostBuilder().UseMqttServer( + mqtt => + { + mqtt.WithDefaultEndpoint().WithKeepAlive(); - var host = new HostBuilder() - .UseMqttServer() + mqtt.ClientConnectedAsync += e => + { + lock (syncLock) + { + connectedClientCount++; + } + + return Task.CompletedTask; + }; + }) .Build(); + await host.StartAsync(); // Perform client connect test @@ -30,8 +43,7 @@ public async Task Default_Host_Configuration() var factory = new MqttFactory(); var client = factory.CreateMqttClient(); var options = factory.CreateClientOptionsBuilder(); - options - .WithTcpServer("127.0.0.1"); + options.WithTcpServer("127.0.0.1"); await client.ConnectAsync(options.Build()); } @@ -39,19 +51,18 @@ public async Task Default_Host_Configuration() { await host.StopAsync(); } + + Assert.AreEqual(1, connectedClientCount); } [TestMethod] public async Task Custom_Host_Configuration() { - - var host = new HostBuilder() - .UseMqttServer(mqtt => - { - mqtt - .WithDefaultEndpoint() - .WithKeepAlive(); - }) + var host = new HostBuilder().UseMqttServer( + mqtt => + { + mqtt.WithDefaultEndpoint().WithKeepAlive(); + }) .Build(); await host.StartAsync(); @@ -61,8 +72,7 @@ public async Task Custom_Host_Configuration() var factory = new MqttFactory(); var client = factory.CreateMqttClient(); var options = factory.CreateClientOptionsBuilder(); - options - .WithTcpServer("127.0.0.1"); + options.WithTcpServer("127.0.0.1"); await client.ConnectAsync(options.Build()); } @@ -73,28 +83,9 @@ public async Task Custom_Host_Configuration() } [TestMethod] - public async Task Advanced_Host_Configuration() + public async Task Default_Host_Configuration() { - var syncLock = new object(); - var connectedClientCount = 0; - var host = new HostBuilder() - .UseMqttServer(mqtt => - { - mqtt - .WithDefaultEndpoint() - .WithKeepAlive(); - - mqtt.ClientConnectedAsync += e => - { - lock (syncLock) - { - connectedClientCount++; - } - return Task.CompletedTask; - }; - }) - .Build(); - + var host = new HostBuilder().UseMqttServer().Build(); await host.StartAsync(); // Perform client connect test @@ -103,8 +94,7 @@ public async Task Advanced_Host_Configuration() var factory = new MqttFactory(); var client = factory.CreateMqttClient(); var options = factory.CreateClientOptionsBuilder(); - options - .WithTcpServer("127.0.0.1"); + options.WithTcpServer("127.0.0.1"); await client.ConnectAsync(options.Build()); } @@ -112,19 +102,16 @@ public async Task Advanced_Host_Configuration() { await host.StopAsync(); } - Assert.AreEqual(1, connectedClientCount); } [TestMethod] public async Task Default_WebSocket_Configuration_Connect() { - var host = new HostBuilder() - .UseMqttServer(mqtt => - { - mqtt - .WithDefaultWebSocketEndpoint() - .WithDefaultWebSocketEndpointPort(8080); - }) + var host = new HostBuilder().UseMqttServer( + mqtt => + { + mqtt.WithDefaultWebSocketEndpoint().WithDefaultWebSocketEndpointPort(8080); + }) .Build(); await host.StartAsync(); @@ -136,8 +123,7 @@ public async Task Default_WebSocket_Configuration_Connect() var factory = new MqttFactory(); var client = factory.CreateMqttClient(); var options = factory.CreateClientOptionsBuilder(); - options - .WithWebSocketServer("127.0.0.1:8080/mqtt"); + options.WithWebSocketServer("127.0.0.1:8080/mqtt"); await client.ConnectAsync(options.Build()); } @@ -152,41 +138,39 @@ public async Task External_HttpListener_WebSocket_Configuration_Connect() { using (var tcs = new CancellationTokenSource()) { - var host = new HostBuilder() - .UseMqttServer() - .Build(); + var host = new HostBuilder().UseMqttServer().Build(); await host.StartAsync(); var httpListener = new HttpListener(); httpListener.Prefixes.Add("http://127.0.0.1:8080/"); httpListener.Start(); - _ = Task.Factory.StartNew(async () => - { - while (!tcs.IsCancellationRequested) + _ = Task.Factory.StartNew( + async () => { - try + while (!tcs.IsCancellationRequested) { - var context = await httpListener.GetContextAsync(); - - if (context.Request.Url.AbsolutePath.Equals("/mqtt", StringComparison.OrdinalIgnoreCase) && context.Request.IsWebSocketRequest) + try { - var mqttServer = host.Services.GetService(); - var webSocketContext = await context.AcceptWebSocketAsync("MQTT"); - mqttServer.HandleWebSocketConnection(webSocketContext, context); + var context = await httpListener.GetContextAsync(); + + if (context.Request.Url.AbsolutePath.Equals("/mqtt", StringComparison.OrdinalIgnoreCase) && context.Request.IsWebSocketRequest) + { + var mqttServer = host.Services.GetService(); + var webSocketContext = await context.AcceptWebSocketAsync("MQTT"); + mqttServer.HandleWebSocketConnection(webSocketContext, context); + } + else + { + context.Response.StatusCode = 404; + context.Response.Close(); + } } - else + catch { - context.Response.StatusCode = 404; - context.Response.Close(); } } - catch - { - - } - } - }); + }); await Task.Delay(5000); @@ -196,8 +180,7 @@ public async Task External_HttpListener_WebSocket_Configuration_Connect() var factory = new MqttFactory(); var client = factory.CreateMqttClient(); var options = factory.CreateClientOptionsBuilder(); - options - .WithWebSocketServer("127.0.0.1:8080/mqtt"); + options.WithWebSocketServer("127.0.0.1:8080/mqtt"); await client.ConnectAsync(options.Build()); } @@ -208,7 +191,6 @@ public async Task External_HttpListener_WebSocket_Configuration_Connect() } } } - } } From 3f2e5637347c589069a7d7dc268953280d71c173 Mon Sep 17 00:00:00 2001 From: Christian <6939810+chkr1011@users.noreply.github.com> Date: Sat, 18 Nov 2023 10:48:46 +0100 Subject: [PATCH 12/16] Apply code style --- .../Server_Hosting_Extensions_Samples.cs | 1 + .../Hosting_Tests.cs | 7 +- .../MQTTnet.Extensions.Hosting.Tests.csproj | 30 +++++++ ...tpWebSocketClientAuthenticationCallback.cs | 4 - .../Extensions/HostBuilderExtensions.cs | 80 +++++++++++-------- .../Extensions/HostingMqttServerExtensions.cs | 22 ++--- .../MqttServerWebSocketConnectionHandler.cs | 46 ++++++----- .../MqttWebSocketServerAdapter.cs | 50 ++++++------ .../MqttWebSocketServerListener.cs | 76 +++++++++--------- .../MQTTnet.Extensions.Hosting.csproj | 1 + .../Options/MqttServerHostingOptions.cs | 13 +-- .../MqttServerTlsWebSocketEndpointOptions.cs | 6 +- .../MqttServerWebSocketEndpointBaseOptions.cs | 5 +- .../MqttServerWebSocketEndpointOptions.cs | 6 +- Source/MQTTnet.Tests/MQTTnet.Tests.csproj | 25 ------ 15 files changed, 190 insertions(+), 182 deletions(-) rename Source/{MQTTnet.Tests/Server => MQTTnet.Extensions.Hosting.Tests}/Hosting_Tests.cs (96%) create mode 100644 Source/MQTTnet.Extensions.Hosting.Tests/MQTTnet.Extensions.Hosting.Tests.csproj diff --git a/Samples/Server/Server_Hosting_Extensions_Samples.cs b/Samples/Server/Server_Hosting_Extensions_Samples.cs index 35633d5f8..97892595a 100644 --- a/Samples/Server/Server_Hosting_Extensions_Samples.cs +++ b/Samples/Server/Server_Hosting_Extensions_Samples.cs @@ -9,6 +9,7 @@ // ReSharper disable MemberCanBeMadeStatic.Local using Microsoft.Extensions.Hosting; +using MQTTnet.Extensions.Hosting.Extensions; namespace MQTTnet.Samples.Server; diff --git a/Source/MQTTnet.Tests/Server/Hosting_Tests.cs b/Source/MQTTnet.Extensions.Hosting.Tests/Hosting_Tests.cs similarity index 96% rename from Source/MQTTnet.Tests/Server/Hosting_Tests.cs rename to Source/MQTTnet.Extensions.Hosting.Tests/Hosting_Tests.cs index ead05a0dc..bf20ef7da 100644 --- a/Source/MQTTnet.Tests/Server/Hosting_Tests.cs +++ b/Source/MQTTnet.Extensions.Hosting.Tests/Hosting_Tests.cs @@ -1,4 +1,4 @@ -#if NET5_0_OR_GREATER +#if NET6_0_OR_GREATER using System; using System.Net; using System.Threading; @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.VisualStudio.TestTools.UnitTesting; +using MQTTnet.Extensions.Hosting.Extensions; using MQTTnet.Server; namespace MQTTnet.Tests.Server @@ -123,7 +124,7 @@ public async Task Default_WebSocket_Configuration_Connect() var factory = new MqttFactory(); var client = factory.CreateMqttClient(); var options = factory.CreateClientOptionsBuilder(); - options.WithWebSocketServer("127.0.0.1:8080/mqtt"); + options.WithWebSocketServer(o => o.WithUri("127.0.0.1:8080/mqtt")); await client.ConnectAsync(options.Build()); } @@ -180,7 +181,7 @@ public async Task External_HttpListener_WebSocket_Configuration_Connect() var factory = new MqttFactory(); var client = factory.CreateMqttClient(); var options = factory.CreateClientOptionsBuilder(); - options.WithWebSocketServer("127.0.0.1:8080/mqtt"); + options.WithWebSocketServer(o => o.WithUri("127.0.0.1:8080/mqtt")); await client.ConnectAsync(options.Build()); } diff --git a/Source/MQTTnet.Extensions.Hosting.Tests/MQTTnet.Extensions.Hosting.Tests.csproj b/Source/MQTTnet.Extensions.Hosting.Tests/MQTTnet.Extensions.Hosting.Tests.csproj new file mode 100644 index 000000000..cbed9b57c --- /dev/null +++ b/Source/MQTTnet.Extensions.Hosting.Tests/MQTTnet.Extensions.Hosting.Tests.csproj @@ -0,0 +1,30 @@ + + + + net6.0 + false + 7.3 + false + false + true + 1591;NETSDK1138 + + + + + + + + + + + 6.0.0 + + + + + + + + + \ No newline at end of file diff --git a/Source/MQTTnet.Extensions.Hosting/Events/HttpWebSocketClientAuthenticationCallback.cs b/Source/MQTTnet.Extensions.Hosting/Events/HttpWebSocketClientAuthenticationCallback.cs index 108fdee27..291859090 100644 --- a/Source/MQTTnet.Extensions.Hosting/Events/HttpWebSocketClientAuthenticationCallback.cs +++ b/Source/MQTTnet.Extensions.Hosting/Events/HttpWebSocketClientAuthenticationCallback.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; namespace MQTTnet.Extensions.Hosting.Events { public delegate Task HttpWebSocketClientAuthenticationCallback(); - } diff --git a/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs b/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs index 2baea6ce3..e7a7cddf1 100644 --- a/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs +++ b/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs @@ -1,61 +1,73 @@ +using System; +using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using MQTTnet.Adapter; using MQTTnet.Diagnostics; -using MQTTnet.Extensions.Hosting; using MQTTnet.Extensions.Hosting.Implementations; using MQTTnet.Extensions.Hosting.Options; using MQTTnet.Implementations; using MQTTnet.Server; -using System; -using System.Collections.Generic; -namespace Microsoft.Extensions.Hosting +namespace MQTTnet.Extensions.Hosting.Extensions { public static class HostBuilderExtensions { - public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder) - => hostBuilder.UseMqttServer(builder => + { + if (hostBuilder == null) { - builder.WithDefaultEndpoint(); - }); + throw new ArgumentNullException(nameof(hostBuilder)); + } + return hostBuilder.UseMqttServer( + builder => + { + builder.WithDefaultEndpoint(); + }); + } public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder, Action configure) { - var startActions = new List>(); - var stopActions = new List>(); - hostBuilder.ConfigureServices((context, services) => + if (hostBuilder == null) { - services.AddSingleton(s => - { - var builder = new MqttServerHostingBuilder(s, startActions, stopActions); - configure(builder); - return builder.Build(); - }); + throw new ArgumentNullException(nameof(hostBuilder)); + } - var logger = new MqttNetEventLogger(); - - services - .AddSingleton(logger) - .AddSingleton() - .AddSingleton() - .AddSingleton(s => s.GetRequiredService()) - .AddSingleton(s => new MqttServerConfigurationHostedService(s, startActions, stopActions)) - .AddSingleton(s => s.GetRequiredService()) + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } - .AddSingleton() - .AddSingleton(s => s.GetRequiredService()) + var startActions = new List>(); + var stopActions = new List>(); - .AddSingleton() + hostBuilder.ConfigureServices( + (context, services) => + { + services.AddSingleton( + s => + { + var builder = new MqttServerHostingBuilder(s, startActions, stopActions); + configure(builder); + return builder.Build(); + }); - .AddSingleton() - .AddSingleton(s => s.GetRequiredService()); + var logger = new MqttNetEventLogger(); - }); + services.AddSingleton(logger) + .AddSingleton() + .AddSingleton() + .AddSingleton(s => s.GetRequiredService()) + .AddSingleton(s => new MqttServerConfigurationHostedService(s, startActions, stopActions)) + .AddSingleton(s => s.GetRequiredService()) + .AddSingleton() + .AddSingleton(s => s.GetRequiredService()) + .AddSingleton() + .AddSingleton() + .AddSingleton(s => s.GetRequiredService()); + }); return hostBuilder; } - } -} +} \ No newline at end of file diff --git a/Source/MQTTnet.Extensions.Hosting/Extensions/HostingMqttServerExtensions.cs b/Source/MQTTnet.Extensions.Hosting/Extensions/HostingMqttServerExtensions.cs index a9dd2fd43..f5dae5562 100644 --- a/Source/MQTTnet.Extensions.Hosting/Extensions/HostingMqttServerExtensions.cs +++ b/Source/MQTTnet.Extensions.Hosting/Extensions/HostingMqttServerExtensions.cs @@ -1,25 +1,27 @@ -using Microsoft.Extensions.DependencyInjection; -using MQTTnet.Extensions.Hosting; -using MQTTnet.Extensions.Hosting.Implementations; -using MQTTnet.Server; using System; -using System.Collections.Generic; using System.Net; using System.Net.WebSockets; -using System.Text; +using Microsoft.Extensions.DependencyInjection; +using MQTTnet.Extensions.Hosting.Implementations; +using MQTTnet.Server; -namespace MQTTnet.Server +namespace MQTTnet.Extensions.Hosting.Extensions { public static class HostingMqttServerExtensions { - public static void HandleWebSocketConnection(this MqttServer server, HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext) { + if (server == null) + { + throw new ArgumentNullException(nameof(server)); + } + if (!(server is MqttHostedServer mqttHostedServer)) + { throw new InvalidOperationException("The server must be started through hosting extensions."); + } mqttHostedServer.ServiceProvider.GetRequiredService().HandleWebSocketConnection(webSocketContext, httpListenerContext); } - } -} +} \ No newline at end of file diff --git a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs index 98aadab87..c5840168d 100644 --- a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs +++ b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs @@ -1,23 +1,21 @@ -using Microsoft.Extensions.Hosting; -using MQTTnet.Adapter; -using MQTTnet.Diagnostics; -using MQTTnet.Formatter; -using MQTTnet.Implementations; using System; -using System.Collections.Generic; using System.Net; using System.Net.WebSockets; using System.Security.Cryptography.X509Certificates; -using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using MQTTnet.Adapter; +using MQTTnet.Diagnostics; +using MQTTnet.Formatter; +using MQTTnet.Implementations; namespace MQTTnet.Extensions.Hosting.Implementations { - public class MqttServerWebSocketConnectionHandler : IHostedService, IDisposable + public sealed class MqttServerWebSocketConnectionHandler : IHostedService, IDisposable { - readonly CancellationTokenSource _cts = new CancellationTokenSource(); readonly MqttWebSocketServerAdapter _adapter; + readonly CancellationTokenSource _cancellationToken = new CancellationTokenSource(); readonly IMqttNetLogger _logger; public MqttServerWebSocketConnectionHandler(MqttWebSocketServerAdapter adapter, IMqttNetLogger logger) @@ -26,26 +24,35 @@ public MqttServerWebSocketConnectionHandler(MqttWebSocketServerAdapter adapter, _logger = logger; } - public Task StartAsync(CancellationToken cancellationToken) + public void Dispose() { - return Task.CompletedTask; + _cancellationToken.Dispose(); } - public Task StopAsync(CancellationToken cancellationToken) + public void HandleWebSocketConnection(HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext, X509Certificate2 clientCertificate = null) { - _cts.Cancel(); + _ = Task.Factory.StartNew(() => TryHandleWebSocketConnectionAsync(webSocketContext, httpListenerContext, clientCertificate)); + } + public Task StartAsync(CancellationToken cancellationToken) + { return Task.CompletedTask; } - public void HandleWebSocketConnection(HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext, X509Certificate2 clientCertificate = null) + public Task StopAsync(CancellationToken cancellationToken) { - _ = Task.Factory.StartNew(() => TryHandleWebSocketConnectionAsync(webSocketContext, httpListenerContext, clientCertificate)); + _cancellationToken.Cancel(); + + return Task.CompletedTask; } async Task TryHandleWebSocketConnectionAsync(HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext, X509Certificate2 clientCertificate) { - if (webSocketContext == null) throw new ArgumentNullException(nameof(webSocketContext)); + if (webSocketContext == null) + { + throw new ArgumentNullException(nameof(webSocketContext)); + } + var endpoint = $"{httpListenerContext.Request.RemoteEndPoint.Address}:{httpListenerContext.Request.RemoteEndPoint.Port}"; try @@ -66,10 +73,5 @@ async Task TryHandleWebSocketConnectionAsync(HttpListenerWebSocketContext webSoc clientCertificate?.Dispose(); } } - - public void Dispose() - { - _cts.Dispose(); - } } -} +} \ No newline at end of file diff --git a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs index 39d193a8c..ad36fe3f9 100644 --- a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs +++ b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs @@ -1,36 +1,45 @@ -using MQTTnet.Adapter; -using MQTTnet.Diagnostics; -using MQTTnet.Internal; -using MQTTnet.Server; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using MQTTnet.Extensions.Hosting.Options; using Microsoft.Extensions.DependencyInjection; +using MQTTnet.Adapter; +using MQTTnet.Diagnostics; +using MQTTnet.Extensions.Hosting.Options; +using MQTTnet.Implementations; +using MQTTnet.Internal; +using MQTTnet.Server; -namespace MQTTnet.Implementations +namespace MQTTnet.Extensions.Hosting.Implementations { - public class MqttWebSocketServerAdapter : IMqttServerAdapter + public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter { + readonly MqttServerHostingOptions _hostingOptions; readonly List _listeners = new List(); readonly IServiceProvider _services; - readonly MqttServerHostingOptions _hostingOptions; - MqttServerOptions _serverOptions; - IMqttNetLogger _logger; public MqttWebSocketServerAdapter(IServiceProvider services, MqttServerHostingOptions hostingOptions) { - _services = services; - _hostingOptions = hostingOptions; + _services = services ?? throw new ArgumentNullException(nameof(services)); + _hostingOptions = hostingOptions ?? throw new ArgumentNullException(nameof(hostingOptions)); } - public Func ClientHandler { get; set; } + public Func? ClientHandler { get; set; } + + public void Dispose() + { + foreach (var listener in _listeners) + { + listener.Dispose(); + } + } public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger) { - _serverOptions = options; - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } if (_hostingOptions.DefaultWebSocketEndpointOptions.IsEnabled) { @@ -61,14 +70,5 @@ public Task StopAsync() return CompletedTask.Instance; } - - public void Dispose() - { - foreach (var listener in _listeners) - { - listener.Dispose(); - } - } - } -} +} \ No newline at end of file diff --git a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerListener.cs b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerListener.cs index fd715ebb4..db710ffd4 100644 --- a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerListener.cs +++ b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerListener.cs @@ -1,36 +1,36 @@ -using Microsoft.Extensions.DependencyInjection; -using MQTTnet.Extensions.Hosting.Implementations; -using MQTTnet.Server; using System; -using System.Collections.Generic; using System.Net; using System.Net.Security; -using System.Net.WebSockets; using System.Security.Cryptography.X509Certificates; -using System.Text; using System.Threading; using System.Threading.Tasks; +using MQTTnet.Extensions.Hosting.Options; +using MQTTnet.Server; -namespace MQTTnet.Implementations +namespace MQTTnet.Extensions.Hosting.Implementations { - public class MqttWebSocketServerListener : IDisposable + public sealed class MqttWebSocketServerListener : IDisposable { - readonly IServiceProvider _serviceProvider; - readonly MqttServerOptions _serverOptions; - readonly MqttServerWebSocketEndpointBaseOptions _endpointOptions; readonly MqttServerWebSocketConnectionHandler _connectionHandler; - private HttpListener _listener; + readonly MqttServerWebSocketEndpointBaseOptions _endpointOptions; + readonly MqttServerOptions _serverOptions; + + HttpListener? _listener; public MqttWebSocketServerListener( - IServiceProvider serviceProvider, MqttServerOptions serverOptions, MqttServerWebSocketEndpointBaseOptions endpointOptions, MqttServerWebSocketConnectionHandler connectionHandler) { - _serviceProvider = serviceProvider; - _serverOptions = serverOptions; - _endpointOptions = endpointOptions; - _connectionHandler = connectionHandler; + _serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions)); + _endpointOptions = endpointOptions ?? throw new ArgumentNullException(nameof(endpointOptions)); + _connectionHandler = connectionHandler ?? throw new ArgumentNullException(nameof(connectionHandler)); + } + + public void Dispose() + { + _listener?.Stop(); + _listener?.Close(); } public bool Start(CancellationToken cancellationToken) @@ -42,28 +42,40 @@ public bool Start(CancellationToken cancellationToken) if (_endpointOptions is MqttServerTlsWebSocketEndpointOptions tlsEndpointOptions) { if (tlsEndpointOptions.BoundInterNetworkAddress != null && tlsEndpointOptions.BoundInterNetworkAddress != IPAddress.Any) + { _listener.Prefixes.Add($"https://{tlsEndpointOptions.BoundInterNetworkAddress}:{tlsEndpointOptions.Port}/"); + } + if (tlsEndpointOptions.BoundInterNetworkV6Address != null && tlsEndpointOptions.BoundInterNetworkV6Address != IPAddress.IPv6Any) + { _listener.Prefixes.Add($"https://{tlsEndpointOptions.BoundInterNetworkV6Address}:{tlsEndpointOptions.Port}/"); - if ((tlsEndpointOptions.BoundInterNetworkAddress == null || - tlsEndpointOptions.BoundInterNetworkAddress == IPAddress.Any) && - (tlsEndpointOptions.BoundInterNetworkV6Address == null || - tlsEndpointOptions.BoundInterNetworkV6Address == IPAddress.IPv6Any)) + } + + if ((tlsEndpointOptions.BoundInterNetworkAddress == null || tlsEndpointOptions.BoundInterNetworkAddress == IPAddress.Any) && + (tlsEndpointOptions.BoundInterNetworkV6Address == null || tlsEndpointOptions.BoundInterNetworkV6Address == IPAddress.IPv6Any)) + { _listener.Prefixes.Add($"https://*:{tlsEndpointOptions.Port}/"); + } } else if (_endpointOptions is MqttServerWebSocketEndpointOptions defaultEndpointOptions) { if (defaultEndpointOptions.BoundInterNetworkAddress != null && defaultEndpointOptions.BoundInterNetworkAddress != IPAddress.Any) + { _listener.Prefixes.Add($"http://{defaultEndpointOptions.BoundInterNetworkAddress}:{defaultEndpointOptions.Port}/"); + } + if (defaultEndpointOptions.BoundInterNetworkV6Address != null && defaultEndpointOptions.BoundInterNetworkV6Address != IPAddress.IPv6Any) + { _listener.Prefixes.Add($"http://{defaultEndpointOptions.BoundInterNetworkV6Address}:{defaultEndpointOptions.Port}/"); - if ((defaultEndpointOptions.BoundInterNetworkAddress == null || - defaultEndpointOptions.BoundInterNetworkAddress == IPAddress.Any) && - (defaultEndpointOptions.BoundInterNetworkV6Address == null || - defaultEndpointOptions.BoundInterNetworkV6Address == IPAddress.IPv6Any)) + } + + if ((defaultEndpointOptions.BoundInterNetworkAddress == null || defaultEndpointOptions.BoundInterNetworkAddress == IPAddress.Any) && + (defaultEndpointOptions.BoundInterNetworkV6Address == null || defaultEndpointOptions.BoundInterNetworkV6Address == IPAddress.IPv6Any)) + { _listener.Prefixes.Add($"http://127.0.0.1:{defaultEndpointOptions.Port}/"); // TODO: Correct this to proper wildcard + } } - + _listener.Start(); Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken); @@ -80,7 +92,7 @@ async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { - var context = await _listener.GetContextAsync(); + var context = await _listener!.GetContextAsync(); if (_serverOptions.TlsEndpointOptions.ClientCertificateRequired) { var clientCertificate = await context.Request.GetClientCertificateAsync().ConfigureAwait(false); @@ -107,13 +119,5 @@ async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken) _connectionHandler.HandleWebSocketConnection(webSocketContext, context); } } - - - public void Dispose() - { - _listener?.Stop(); - _listener?.Close(); - } - } -} +} \ No newline at end of file diff --git a/Source/MQTTnet.Extensions.Hosting/MQTTnet.Extensions.Hosting.csproj b/Source/MQTTnet.Extensions.Hosting/MQTTnet.Extensions.Hosting.csproj index da7acdb4e..9fbad72bd 100644 --- a/Source/MQTTnet.Extensions.Hosting/MQTTnet.Extensions.Hosting.csproj +++ b/Source/MQTTnet.Extensions.Hosting/MQTTnet.Extensions.Hosting.csproj @@ -3,6 +3,7 @@ netcoreapp3.1;net5.0;net6.0;net7.0 enable + NETSDK1138 diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs index f4c6a7bd1..b35af7eb5 100644 --- a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs @@ -1,22 +1,17 @@ using MQTTnet.Extensions.Hosting.Events; -using MQTTnet.Server; -using System; -using System.Collections.Generic; -using System.Text; namespace MQTTnet.Extensions.Hosting.Options { - public class MqttServerHostingOptions + public sealed class MqttServerHostingOptions { public bool AutoRemoveEventHandlers { get; set; } = true; - public MqttServerWebSocketEndpointOptions DefaultWebSocketEndpointOptions { get; } = new MqttServerWebSocketEndpointOptions(); - public MqttServerTlsWebSocketEndpointOptions DefaultTlsWebSocketEndpointOptions { get; } = new MqttServerTlsWebSocketEndpointOptions(); + public MqttServerWebSocketEndpointOptions DefaultWebSocketEndpointOptions { get; } = new MqttServerWebSocketEndpointOptions(); + public HttpWebSocketClientAuthenticationCallback WebSocketAuthenticationCallback { get; set; } public string WebSocketRoute { get; set; } - } -} +} \ No newline at end of file diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerTlsWebSocketEndpointOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerTlsWebSocketEndpointOptions.cs index b1bf6508e..2a982d73a 100644 --- a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerTlsWebSocketEndpointOptions.cs +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerTlsWebSocketEndpointOptions.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace MQTTnet.Server +namespace MQTTnet.Extensions.Hosting.Options { public class MqttServerTlsWebSocketEndpointOptions : MqttServerWebSocketEndpointBaseOptions { diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointBaseOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointBaseOptions.cs index 461d30196..fe64dee70 100644 --- a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointBaseOptions.cs +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointBaseOptions.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; using System.Net; -using System.Text; -namespace MQTTnet.Server +namespace MQTTnet.Extensions.Hosting.Options { public abstract class MqttServerWebSocketEndpointBaseOptions { diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointOptions.cs index 47d115899..e7b8c14ec 100644 --- a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointOptions.cs +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerWebSocketEndpointOptions.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace MQTTnet.Server +namespace MQTTnet.Extensions.Hosting.Options { public class MqttServerWebSocketEndpointOptions : MqttServerWebSocketEndpointBaseOptions { diff --git a/Source/MQTTnet.Tests/MQTTnet.Tests.csproj b/Source/MQTTnet.Tests/MQTTnet.Tests.csproj index 408700f13..c19b803fe 100644 --- a/Source/MQTTnet.Tests/MQTTnet.Tests.csproj +++ b/Source/MQTTnet.Tests/MQTTnet.Tests.csproj @@ -22,31 +22,6 @@ - - - - - - 3.1.0 - - - - - - 5.0.0 - - - - - - 6.0.0 - - - - - - 7.0.0 - \ No newline at end of file From 9f99d4038de2c5f542483a55f918d16083fe3414 Mon Sep 17 00:00:00 2001 From: Christian <6939810+chkr1011@users.noreply.github.com> Date: Sat, 18 Nov 2023 10:48:51 +0100 Subject: [PATCH 13/16] Move projects --- MQTTnet.sln | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MQTTnet.sln b/MQTTnet.sln index e8739565c..b6a6636c1 100644 --- a/MQTTnet.sln +++ b/MQTTnet.sln @@ -34,6 +34,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.AspTestApp", "Sourc EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Hosting", "Source\MQTTnet.Extensions.Hosting\MQTTnet.Extensions.Hosting.csproj", "{B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{26138A7E-435D-4C37-92B8-F506C640266D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Extensions.Hosting.Tests", "Source\MQTTnet.Extensions.Hosting.Tests\MQTTnet.Extensions.Hosting.Tests.csproj", "{C4DE2742-2177-4AD0-BC01-74F29E9595C3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -88,11 +92,21 @@ Global {B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}.Debug|Any CPU.Build.0 = Debug|Any CPU {B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}.Release|Any CPU.ActiveCfg = Release|Any CPU {B53FC20B-862C-4F8F-B9FF-E8C8D76A870D}.Release|Any CPU.Build.0 = Release|Any CPU + {C4DE2742-2177-4AD0-BC01-74F29E9595C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4DE2742-2177-4AD0-BC01-74F29E9595C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4DE2742-2177-4AD0-BC01-74F29E9595C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4DE2742-2177-4AD0-BC01-74F29E9595C3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {72867E4C-4E15-4E8E-8FAB-AE9253286BBC} = {26138A7E-435D-4C37-92B8-F506C640266D} + {A238BBBF-C75F-482D-9CC3-BB34ABA9B675} = {26138A7E-435D-4C37-92B8-F506C640266D} + {B270F32A-9F3E-42EE-A989-813E35E29ADB} = {26138A7E-435D-4C37-92B8-F506C640266D} + {175D5340-CC5B-4542-939D-4E7D15A0BC8D} = {26138A7E-435D-4C37-92B8-F506C640266D} + {C4DE2742-2177-4AD0-BC01-74F29E9595C3} = {26138A7E-435D-4C37-92B8-F506C640266D} + {2F516E76-AAC4-4219-B7D1-34CDD3CFF381} = {26138A7E-435D-4C37-92B8-F506C640266D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894} From 7605d10aa6e3d3f3beeeaab691bf40254ce6eb6c Mon Sep 17 00:00:00 2001 From: kallayj <118477617+kallayj@users.noreply.github.com> Date: Mon, 20 Nov 2023 19:06:57 +0000 Subject: [PATCH 14/16] Fixes nullability of method parameters. --- .../Implementations/MqttServerWebSocketConnectionHandler.cs | 4 ++-- .../Options/MqttServerHostingOptions.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs index c5840168d..9f9bb647b 100644 --- a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs +++ b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttServerWebSocketConnectionHandler.cs @@ -29,7 +29,7 @@ public void Dispose() _cancellationToken.Dispose(); } - public void HandleWebSocketConnection(HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext, X509Certificate2 clientCertificate = null) + public void HandleWebSocketConnection(HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext, X509Certificate2? clientCertificate = null) { _ = Task.Factory.StartNew(() => TryHandleWebSocketConnectionAsync(webSocketContext, httpListenerContext, clientCertificate)); } @@ -46,7 +46,7 @@ public Task StopAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - async Task TryHandleWebSocketConnectionAsync(HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext, X509Certificate2 clientCertificate) + async Task TryHandleWebSocketConnectionAsync(HttpListenerWebSocketContext webSocketContext, HttpListenerContext httpListenerContext, X509Certificate2? clientCertificate) { if (webSocketContext == null) { diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs index b35af7eb5..d34782c54 100644 --- a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs @@ -10,8 +10,8 @@ public sealed class MqttServerHostingOptions public MqttServerWebSocketEndpointOptions DefaultWebSocketEndpointOptions { get; } = new MqttServerWebSocketEndpointOptions(); - public HttpWebSocketClientAuthenticationCallback WebSocketAuthenticationCallback { get; set; } + public HttpWebSocketClientAuthenticationCallback? WebSocketAuthenticationCallback { get; set; } - public string WebSocketRoute { get; set; } + public string? WebSocketRoute { get; set; } } } \ No newline at end of file From d6b4b29757cbda70afeee8f8b8d71bce7ec35c9a Mon Sep 17 00:00:00 2001 From: Christian <6939810+chkr1011@users.noreply.github.com> Date: Sat, 25 Nov 2023 16:24:29 +0100 Subject: [PATCH 15/16] Fix breaking changes --- .../Extensions/HostBuilderExtensions.cs | 5 ++++- .../Implementations/MqttWebSocketServerAdapter.cs | 1 - Source/MQTTnet.Extensions.Hosting/MqttHostedServer.cs | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs b/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs index e7a7cddf1..23e383532 100644 --- a/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs +++ b/Source/MQTTnet.Extensions.Hosting/Extensions/HostBuilderExtensions.cs @@ -43,7 +43,7 @@ public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder, Action>(); hostBuilder.ConfigureServices( - (context, services) => + (_, services) => { services.AddSingleton( s => @@ -57,6 +57,8 @@ public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder, Action(logger) .AddSingleton() + .AddSingleton(new MqttNetNullLogger()) + .AddSingleton(new MqttFactory()) .AddSingleton() .AddSingleton(s => s.GetRequiredService()) .AddSingleton(s => new MqttServerConfigurationHostedService(s, startActions, stopActions)) @@ -67,6 +69,7 @@ public static IHostBuilder UseMqttServer(this IHostBuilder hostBuilder, Action() .AddSingleton(s => s.GetRequiredService()); }); + return hostBuilder; } } diff --git a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs index ad36fe3f9..cd68eac3c 100644 --- a/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs +++ b/Source/MQTTnet.Extensions.Hosting/Implementations/MqttWebSocketServerAdapter.cs @@ -6,7 +6,6 @@ using MQTTnet.Adapter; using MQTTnet.Diagnostics; using MQTTnet.Extensions.Hosting.Options; -using MQTTnet.Implementations; using MQTTnet.Internal; using MQTTnet.Server; diff --git a/Source/MQTTnet.Extensions.Hosting/MqttHostedServer.cs b/Source/MQTTnet.Extensions.Hosting/MqttHostedServer.cs index 724e074c6..ccdbf92cd 100644 --- a/Source/MQTTnet.Extensions.Hosting/MqttHostedServer.cs +++ b/Source/MQTTnet.Extensions.Hosting/MqttHostedServer.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using System.Threading; using MQTTnet.Server; @@ -27,7 +26,7 @@ public Task StartAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken) { - return StopAsync(); + return StopAsync(new MqttServerStopOptions()); } } } From 2e4517441c7935ceccd21be988933e9951666fd9 Mon Sep 17 00:00:00 2001 From: kallayj <118477617+kallayj@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:00:10 +0000 Subject: [PATCH 16/16] Removes unused properties. --- .../MqttServerHostingBuilder.cs | 14 -------------- .../Options/MqttServerHostingOptions.cs | 4 ---- 2 files changed, 18 deletions(-) diff --git a/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs b/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs index 63379a208..e8b2a6ff9 100644 --- a/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs +++ b/Source/MQTTnet.Extensions.Hosting/MqttServerHostingBuilder.cs @@ -334,19 +334,5 @@ public MqttServerHostingBuilder WithoutDefaultWebSocketEndpoint() return this; } - - public MqttServerHostingBuilder WithWebSocketClientAuthentication(HttpWebSocketClientAuthenticationCallback callback) - { - _hostingOptions.WebSocketAuthenticationCallback = callback; - - return this; - } - - public MqttServerHostingBuilder WithWebSocketRoute(string value) - { - _hostingOptions.WebSocketRoute = value; - - return this; - } } } \ No newline at end of file diff --git a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs index d34782c54..4884a093c 100644 --- a/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs +++ b/Source/MQTTnet.Extensions.Hosting/Options/MqttServerHostingOptions.cs @@ -9,9 +9,5 @@ public sealed class MqttServerHostingOptions public MqttServerTlsWebSocketEndpointOptions DefaultTlsWebSocketEndpointOptions { get; } = new MqttServerTlsWebSocketEndpointOptions(); public MqttServerWebSocketEndpointOptions DefaultWebSocketEndpointOptions { get; } = new MqttServerWebSocketEndpointOptions(); - - public HttpWebSocketClientAuthenticationCallback? WebSocketAuthenticationCallback { get; set; } - - public string? WebSocketRoute { get; set; } } } \ No newline at end of file