mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-18 10:26:11 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a26b641284 | ||
|
|
7be3685850 | ||
|
|
1a6be47144 | ||
|
|
5d73de2a86 | ||
|
|
2eaebe0e6a | ||
|
|
d7b552fce2 | ||
|
|
bea6d75251 | ||
|
|
f7db00c08e | ||
|
|
f259bbcc9b | ||
|
|
252772007b | ||
|
|
21a9974e5d | ||
|
|
d63143bdc9 | ||
|
|
aad9eac361 | ||
|
|
e8a683b936 | ||
|
|
e475e36ce7 | ||
|
|
611c230da2 | ||
|
|
18839de083 | ||
|
|
194664bc9c | ||
|
|
10a33fd153 | ||
|
|
868aa7d89a | ||
|
|
1f8792cf87 | ||
|
|
7bb99c8b37 | ||
|
|
7f5504b01b | ||
|
|
fd789417e7 | ||
|
|
f1f9924bce | ||
|
|
b0c603082b | ||
|
|
f3f10e9f65 | ||
|
|
beb228cc05 | ||
|
|
7a7f0640ba | ||
|
|
55d009ce6b | ||
|
|
e23ac4f703 | ||
|
|
fc367ddb64 | ||
|
|
cf217034fa | ||
|
|
6b6627f823 | ||
|
|
4f51a238b3 | ||
|
|
733c587e45 | ||
|
|
3260920828 | ||
|
|
4e6aeb91a4 | ||
|
|
5056843611 | ||
|
|
a65c015a58 | ||
|
|
e63f26789f | ||
|
|
9a598892a7 | ||
|
|
857ab005be | ||
|
|
33e4ec1943 | ||
|
|
6a564a0c72 | ||
|
|
3a4dce5080 | ||
|
|
4e6118d324 | ||
|
|
269bfa09ac | ||
|
|
59dd6b98a3 | ||
|
|
9f37306f20 | ||
|
|
85b000c6b7 | ||
|
|
5a919ce5d2 | ||
|
|
57ab8e5abc | ||
|
|
8f6d48be81 | ||
|
|
38cfa15cc3 | ||
|
|
6adf7096ec | ||
|
|
df149eb0b3 | ||
|
|
63c9329190 | ||
|
|
246a10649a | ||
|
|
0a76ac15c6 | ||
|
|
d801af5616 | ||
|
|
257211fc7a | ||
|
|
a407fc381b | ||
|
|
4fa275bf2b | ||
|
|
7cf12dc751 | ||
|
|
40aefd0ad3 | ||
|
|
c20142671f | ||
|
|
75dfe68a94 | ||
|
|
92518fc4ec | ||
|
|
16180e83ba | ||
|
|
c779b394f1 | ||
|
|
e60a732b61 | ||
|
|
7ea9415c63 | ||
|
|
13c399bc9a | ||
|
|
4565c3990a | ||
|
|
2bd2648b48 | ||
|
|
1b56155f1c | ||
|
|
626930cfae | ||
|
|
6cf9cb0af2 | ||
|
|
60941c04e5 | ||
|
|
ba68b1274b | ||
|
|
dbe12b9c48 | ||
|
|
52b79a140b | ||
|
|
c270225e9e | ||
|
|
fc3965f5a3 | ||
|
|
1d553c5ece | ||
|
|
07ad9fc557 | ||
|
|
88e8691ce0 | ||
|
|
28641892f5 |
@@ -13,7 +13,7 @@
|
|||||||
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
|
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
|
||||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<group targetFramework="netstandard2.0">
|
<group targetFramework="netstandard2.0">
|
||||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
|
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
|
||||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
||||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<description>This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.</description>
|
<description>This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.</description>
|
||||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<group targetFramework="netstandard1.3">
|
<group targetFramework="netstandard1.3">
|
||||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||||
|
|||||||
+17
-4
@@ -12,12 +12,16 @@
|
|||||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</description>
|
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</description>
|
||||||
<releaseNotes>
|
<releaseNotes>
|
||||||
* [Client] Fixed issue in keep alive handling.
|
* [Client] Fixed wrong value for "ClientWasConnected" in "MqttClientDisconnectedEventArgs" #976 (thanks to @dbeinder).
|
||||||
* [MQTTnet.Server] Added support for delivering static files (HTML, JavaScript etc.).
|
* [Client] Added direct support for Amazon AWS connections (requires .NET Core 3.1) (thanks to @henning-krause).
|
||||||
* [MQTTnet.Server] Fixed web socket protocol errors (when using paho JS etc.).
|
* [Client] handle disconnect package and propagate disconnect reason code (thanks to @JanEggers)
|
||||||
|
* [ManagedClient] Exposed the internal MQTT client.
|
||||||
|
* [Server] Added client message queue interceptor for QoS level (thanks to @msallin).
|
||||||
|
* [Server] improved behavior when multiple connections fight over a session (thanks to @JanEggers)
|
||||||
|
* [Server] Exposed _ClientDisconnectedInterceptor_ (thanks to @SeppPenner).
|
||||||
</releaseNotes>
|
</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
<copyright>Copyright Christian Kratky 2016-2020</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<group targetFramework="netstandard1.3">
|
<group targetFramework="netstandard1.3">
|
||||||
<dependency id="NETStandard.Library" version="1.3.0" />
|
<dependency id="NETStandard.Library" version="1.3.0" />
|
||||||
@@ -37,6 +41,12 @@
|
|||||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||||
</group>
|
</group>
|
||||||
|
<group targetFramework="netcoreapp3.1">
|
||||||
|
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||||
|
<dependency id="System.Net.Security" version="4.3.2" />
|
||||||
|
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||||
|
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||||
|
</group>
|
||||||
<group targetFramework="uap10.0">
|
<group targetFramework="uap10.0">
|
||||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" />
|
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" />
|
||||||
</group>
|
</group>
|
||||||
@@ -59,6 +69,9 @@
|
|||||||
<!-- .NET Standard 2.1 -->
|
<!-- .NET Standard 2.1 -->
|
||||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.1\MQTTnet.*" target="lib\netstandard2.1\"/>
|
<file src="..\Source\MQTTnet\bin\Release\netstandard2.1\MQTTnet.*" target="lib\netstandard2.1\"/>
|
||||||
|
|
||||||
|
<!-- .NET Standard 3.1 -->
|
||||||
|
<file src="..\Source\MQTTnet\bin\Release\netcoreapp3.1\MQTTnet.*" target="lib\netcoreapp3.1\"/>
|
||||||
|
|
||||||
<!-- Universal Windows -->
|
<!-- Universal Windows -->
|
||||||
<file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/>
|
<file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/>
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
|
|||||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||||
|
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||||
|
|
||||||
# Build the ASP.NET Core 2.0 extension
|
# Build the ASP.NET Core 2.0 extension
|
||||||
|
|||||||
+60
@@ -55,6 +55,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocke
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Apps", "Apps", "{A56E3128-1639-4F31-873A-325E14BB6295}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Apps", "Apps", "{A56E3128-1639-4F31-873A-325E14BB6295}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Test.BlazorApp.Server", "Tests\MQTTnet.Test.BlazorApp\Server\MQTTnet.Test.BlazorApp.Server.csproj", "{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Test.BlazorApp.Client", "Tests\MQTTnet.Test.BlazorApp\Client\MQTTnet.Test.BlazorApp.Client.csproj", "{D260D63D-7902-4C55-9665-84C5CACBBB24}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Test.BlazorApp.Shared", "Tests\MQTTnet.Test.BlazorApp\Shared\MQTTnet.Test.BlazorApp.Shared.csproj", "{DDB069BA-6E1A-48C7-B374-5D903B553CAD}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -266,6 +274,54 @@ Global
|
|||||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x64.Build.0 = Release|Any CPU
|
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x86.ActiveCfg = Release|Any CPU
|
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x86.Build.0 = Release|Any CPU
|
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|ARM.Build.0 = Release|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|ARM.Build.0 = Release|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|ARM.Build.0 = Release|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -283,6 +339,10 @@ Global
|
|||||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B} = {A56E3128-1639-4F31-873A-325E14BB6295}
|
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B} = {A56E3128-1639-4F31-873A-325E14BB6295}
|
||||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
{2BD01D53-4CA5-4142-BE8D-313876395E3E} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||||
|
{61B165A0-5AA8-4E04-A53D-A22A84AA6EB7} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||||
|
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F} = {61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}
|
||||||
|
{D260D63D-7902-4C55-9665-84C5CACBBB24} = {61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}
|
||||||
|
{DDB069BA-6E1A-48C7-B374-5D903B553CAD} = {61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ _MQTTnet Server_ is a standalone cross platform MQTT server (like mosquitto) bas
|
|||||||
* Universal Windows Platform (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU, Windows 10 IoT Core)
|
* Universal Windows Platform (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU, Windows 10 IoT Core)
|
||||||
* Xamarin.Android 7.5+
|
* Xamarin.Android 7.5+
|
||||||
* Xamarin.iOS 10.14+
|
* Xamarin.iOS 10.14+
|
||||||
|
* Blazor WebAssembly 3.2.0
|
||||||
|
|
||||||
## Supported MQTT versions
|
## Supported MQTT versions
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Builder;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
namespace MQTTnet.AspNetCore.Extensions
|
||||||
{
|
{
|
||||||
public static class ApplicationBuilderExtensions
|
public static class ApplicationBuilderExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Connections;
|
using Microsoft.AspNetCore.Connections;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
namespace MQTTnet.AspNetCore.Extensions
|
||||||
{
|
{
|
||||||
public static class ConnectionBuilderExtensions
|
public static class ConnectionBuilderExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Connections;
|
using Microsoft.AspNetCore.Http.Connections;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
#if NETCOREAPP3_1
|
||||||
|
using System;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace MQTTnet.AspNetCore.Extensions
|
||||||
{
|
{
|
||||||
public static class ConnectionRouteBuilderExtensions
|
public static class ConnectionRouteBuilderExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using MQTTnet.Exceptions;
|
|||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
namespace MQTTnet.AspNetCore.Extensions
|
||||||
{
|
{
|
||||||
public static class ReaderExtensions
|
public static class ReaderExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using System;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
namespace MQTTnet.AspNetCore.Extensions
|
||||||
{
|
{
|
||||||
public static class ServiceCollectionExtensions
|
public static class ServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,8 +8,15 @@
|
|||||||
<PackageId />
|
<PackageId />
|
||||||
<LangVersion>7.2</LangVersion>
|
<LangVersion>7.2</LangVersion>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
|
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -19,7 +26,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1'">
|
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1'">
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -10,11 +10,19 @@ using System.IO.Pipelines;
|
|||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.AspNetCore.Extensions;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
namespace MQTTnet.AspNetCore
|
||||||
{
|
{
|
||||||
public class MqttConnectionContext : IMqttChannelAdapter
|
public sealed class MqttConnectionContext : IMqttChannelAdapter
|
||||||
{
|
{
|
||||||
|
readonly AsyncLock _writerLock = new AsyncLock();
|
||||||
|
readonly SpanBasedMqttPacketBodyReader _reader;
|
||||||
|
|
||||||
|
PipeReader _input;
|
||||||
|
PipeWriter _output;
|
||||||
|
|
||||||
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
|
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
|
||||||
{
|
{
|
||||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||||
@@ -29,10 +37,6 @@ namespace MQTTnet.AspNetCore
|
|||||||
_reader = new SpanBasedMqttPacketBodyReader();
|
_reader = new SpanBasedMqttPacketBodyReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
private PipeReader _input;
|
|
||||||
private PipeWriter _output;
|
|
||||||
private readonly SpanBasedMqttPacketBodyReader _reader;
|
|
||||||
|
|
||||||
public string Endpoint
|
public string Endpoint
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -57,9 +61,8 @@ namespace MQTTnet.AspNetCore
|
|||||||
|
|
||||||
public X509Certificate2 ClientCertificate => Http?.HttpContext?.Connection?.ClientCertificate;
|
public X509Certificate2 ClientCertificate => Http?.HttpContext?.Connection?.ClientCertificate;
|
||||||
|
|
||||||
private IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
|
|
||||||
|
|
||||||
public ConnectionContext Connection { get; }
|
public ConnectionContext Connection { get; }
|
||||||
|
|
||||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||||
|
|
||||||
public long BytesSent { get; set; }
|
public long BytesSent { get; set; }
|
||||||
@@ -68,7 +71,7 @@ namespace MQTTnet.AspNetCore
|
|||||||
public Action ReadingPacketStartedCallback { get; set; }
|
public Action ReadingPacketStartedCallback { get; set; }
|
||||||
public Action ReadingPacketCompletedCallback { get; set; }
|
public Action ReadingPacketCompletedCallback { get; set; }
|
||||||
|
|
||||||
private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
|
IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
|
||||||
|
|
||||||
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -142,6 +145,13 @@ namespace MQTTnet.AspNetCore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// completing the cannels makes sure that there is no more data read after a protocol error
|
||||||
|
_input?.Complete(e);
|
||||||
|
_output?.Complete(e);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
ReadingPacketCompletedCallback?.Invoke();
|
ReadingPacketCompletedCallback?.Invoke();
|
||||||
@@ -160,25 +170,18 @@ namespace MQTTnet.AspNetCore
|
|||||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var formatter = PacketFormatterAdapter;
|
var formatter = PacketFormatterAdapter;
|
||||||
var buffer = formatter.Encode(packet);
|
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
|
||||||
var msg = buffer.AsMemory();
|
|
||||||
var output = _output;
|
|
||||||
|
|
||||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
|
var buffer = formatter.Encode(packet);
|
||||||
|
var msg = buffer.AsMemory();
|
||||||
|
var output = _output;
|
||||||
var result = await output.WriteAsync(msg, cancellationToken).ConfigureAwait(false);
|
var result = await output.WriteAsync(msg, cancellationToken).ConfigureAwait(false);
|
||||||
if (result.IsCompleted)
|
if (result.IsCompleted)
|
||||||
{
|
{
|
||||||
BytesSent += msg.Length;
|
BytesSent += msg.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketFormatterAdapter.FreeBuffer();
|
PacketFormatterAdapter.FreeBuffer();
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
_writerSemaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -1,12 +1,29 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
namespace MQTTnet.AspNetCore
|
||||||
{
|
{
|
||||||
public static class MqttSubProtocolSelector
|
public static class MqttSubProtocolSelector
|
||||||
{
|
{
|
||||||
|
public static string SelectSubProtocol(HttpRequest request)
|
||||||
|
{
|
||||||
|
if (request == null) throw new ArgumentNullException(nameof(request));
|
||||||
|
|
||||||
|
string subProtocol = null;
|
||||||
|
if (request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
|
||||||
|
{
|
||||||
|
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
|
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
|
||||||
{
|
{
|
||||||
|
if (requestedSubProtocolValues == null) throw new ArgumentNullException(nameof(requestedSubProtocolValues));
|
||||||
|
|
||||||
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
||||||
return requestedSubProtocolValues
|
return requestedSubProtocolValues
|
||||||
.OrderByDescending(p => p.Length)
|
.OrderByDescending(p => p.Length)
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ namespace MQTTnet.AspNetCore
|
|||||||
{
|
{
|
||||||
public class SpanBasedMqttPacketBodyReader : IMqttPacketBodyReader
|
public class SpanBasedMqttPacketBodyReader : IMqttPacketBodyReader
|
||||||
{
|
{
|
||||||
private ReadOnlyMemory<byte> _buffer;
|
ReadOnlyMemory<byte> _buffer;
|
||||||
|
|
||||||
private int _offset;
|
int _offset;
|
||||||
|
|
||||||
public void SetBuffer(ReadOnlyMemory<byte> buffer)
|
public void SetBuffer(ReadOnlyMemory<byte> buffer)
|
||||||
{
|
{
|
||||||
@@ -38,16 +38,6 @@ namespace MQTTnet.AspNetCore
|
|||||||
{
|
{
|
||||||
return ReadSegmentWithLengthPrefix().ToArray();
|
return ReadSegmentWithLengthPrefix().ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlySpan<byte> ReadSegmentWithLengthPrefix()
|
|
||||||
{
|
|
||||||
var span = _buffer.Span;
|
|
||||||
var length = BinaryPrimitives.ReadUInt16BigEndian(span.Slice(_offset));
|
|
||||||
|
|
||||||
var result = span.Slice(_offset+2, length);
|
|
||||||
_offset += 2 + length;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe string ReadStringWithLengthPrefix()
|
public unsafe string ReadStringWithLengthPrefix()
|
||||||
{
|
{
|
||||||
@@ -121,5 +111,19 @@ namespace MQTTnet.AspNetCore
|
|||||||
{
|
{
|
||||||
_offset = position;
|
_offset = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> ReadSegmentWithLengthPrefix()
|
||||||
|
{
|
||||||
|
var span = _buffer.Span;
|
||||||
|
var length = BinaryPrimitives.ReadUInt16BigEndian(span.Slice(_offset));
|
||||||
|
|
||||||
|
if (Length < _offset + length)
|
||||||
|
{
|
||||||
|
throw new MqttProtocolViolationException($"Expected at least {_offset + 2 + length} bytes but there are only {Length} bytes");
|
||||||
|
}
|
||||||
|
var result = span.Slice(_offset + 2, length);
|
||||||
|
_offset += 2 + length;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,15 @@ namespace MQTTnet.AspNetCore
|
|||||||
{
|
{
|
||||||
public class SpanBasedMqttPacketWriter : IMqttPacketWriter
|
public class SpanBasedMqttPacketWriter : IMqttPacketWriter
|
||||||
{
|
{
|
||||||
private readonly ArrayPool<byte> _pool;
|
readonly ArrayPool<byte> _pool = ArrayPool<byte>.Create();
|
||||||
|
|
||||||
public SpanBasedMqttPacketWriter()
|
public SpanBasedMqttPacketWriter()
|
||||||
{
|
{
|
||||||
_pool = ArrayPool<byte>.Create();
|
Reset(0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] _buffer;
|
byte[] _buffer;
|
||||||
private int _position;
|
int _position;
|
||||||
|
|
||||||
public int Length { get; set; }
|
public int Length { get; set; }
|
||||||
|
|
||||||
@@ -112,7 +111,7 @@ namespace MQTTnet.AspNetCore
|
|||||||
Commit(payload.Length);
|
Commit(payload.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Commit(int count)
|
void Commit(int count)
|
||||||
{
|
{
|
||||||
if (_position == Length)
|
if (_position == Length)
|
||||||
{
|
{
|
||||||
@@ -122,7 +121,7 @@ namespace MQTTnet.AspNetCore
|
|||||||
_position += count;
|
_position += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GrowIfNeeded(int requiredAdditional)
|
void GrowIfNeeded(int requiredAdditional)
|
||||||
{
|
{
|
||||||
var requiredTotal = _position + requiredAdditional;
|
var requiredTotal = _position + requiredAdditional;
|
||||||
if (_buffer.Length >= requiredTotal)
|
if (_buffer.Length >= requiredTotal)
|
||||||
|
|||||||
@@ -4,30 +4,46 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Client;
|
||||||
|
|
||||||
namespace MQTTnet.Extensions.ManagedClient
|
namespace MQTTnet.Extensions.ManagedClient
|
||||||
{
|
{
|
||||||
public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
|
public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
|
||||||
{
|
{
|
||||||
bool IsStarted { get; }
|
/// <summary>
|
||||||
bool IsConnected { get; }
|
/// Gets the internally used MQTT client.
|
||||||
int PendingApplicationMessagesCount { get; }
|
/// This property should be used with caution because manipulating the internal client might break the managed client.
|
||||||
IManagedMqttClientOptions Options { get; }
|
/// </summary>
|
||||||
|
IMqttClient InternalClient { get; }
|
||||||
|
|
||||||
|
bool IsStarted { get; }
|
||||||
|
|
||||||
|
bool IsConnected { get; }
|
||||||
|
|
||||||
|
int PendingApplicationMessagesCount { get; }
|
||||||
|
|
||||||
|
IManagedMqttClientOptions Options { get; }
|
||||||
|
|
||||||
IMqttClientConnectedHandler ConnectedHandler { get; set; }
|
IMqttClientConnectedHandler ConnectedHandler { get; set; }
|
||||||
|
|
||||||
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
|
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
|
||||||
|
|
||||||
IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
|
IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
|
||||||
|
|
||||||
IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
|
IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
|
||||||
|
|
||||||
IConnectingFailedHandler ConnectingFailedHandler { get; set; }
|
IConnectingFailedHandler ConnectingFailedHandler { get; set; }
|
||||||
|
|
||||||
ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
|
ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
|
||||||
|
|
||||||
Task StartAsync(IManagedMqttClientOptions options);
|
Task StartAsync(IManagedMqttClientOptions options);
|
||||||
|
|
||||||
Task StopAsync();
|
Task StopAsync();
|
||||||
|
|
||||||
Task PingAsync(CancellationToken cancellationToken);
|
Task PingAsync(CancellationToken cancellationToken);
|
||||||
|
|
||||||
Task SubscribeAsync(IEnumerable<MqttTopicFilter> topicFilters);
|
Task SubscribeAsync(IEnumerable<MqttTopicFilter> topicFilters);
|
||||||
|
|
||||||
Task UnsubscribeAsync(IEnumerable<string> topics);
|
Task UnsubscribeAsync(IEnumerable<string> topics);
|
||||||
|
|
||||||
Task PublishAsync(ManagedMqttApplicationMessage applicationMessages);
|
Task PublishAsync(ManagedMqttApplicationMessage applicationMessages);
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
IMqttClientOptions ClientOptions { get; }
|
IMqttClientOptions ClientOptions { get; }
|
||||||
|
|
||||||
|
bool AutoReconnect { get; }
|
||||||
|
|
||||||
TimeSpan AutoReconnectDelay { get; }
|
TimeSpan AutoReconnectDelay { get; }
|
||||||
|
|
||||||
TimeSpan ConnectionCheckInterval { get; }
|
TimeSpan ConnectionCheckInterval { get; }
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
<Company />
|
<Company />
|
||||||
<Authors />
|
<Authors />
|
||||||
<PackageId />
|
<PackageId />
|
||||||
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
||||||
@@ -23,6 +26,10 @@
|
|||||||
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
|
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
public class ManagedMqttClient : Disposable, IManagedMqttClient
|
public class ManagedMqttClient : Disposable, IManagedMqttClient
|
||||||
{
|
{
|
||||||
private readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>();
|
readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The subscriptions are managed in 2 separate buckets:
|
/// The subscriptions are managed in 2 separate buckets:
|
||||||
@@ -27,33 +27,39 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
/// any thread and are therefore mutex'ed. <see cref="_reconnectSubscriptions"/> get sent to the broker
|
/// any thread and are therefore mutex'ed. <see cref="_reconnectSubscriptions"/> get sent to the broker
|
||||||
/// at reconnect and are solely owned by <see cref="MaintainConnectionAsync"/>.
|
/// at reconnect and are solely owned by <see cref="MaintainConnectionAsync"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Dictionary<string, MqttQualityOfServiceLevel> _reconnectSubscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
readonly Dictionary<string, MqttQualityOfServiceLevel> _reconnectSubscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
||||||
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
||||||
private readonly HashSet<string> _unsubscriptions = new HashSet<string>();
|
readonly HashSet<string> _unsubscriptions = new HashSet<string>();
|
||||||
private readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
|
readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
|
||||||
|
|
||||||
private readonly IMqttClient _mqttClient;
|
readonly IMqttClient _mqttClient;
|
||||||
private readonly IMqttNetScopedLogger _logger;
|
readonly IMqttNetScopedLogger _logger;
|
||||||
|
|
||||||
private readonly AsyncLock _messageQueueLock = new AsyncLock();
|
readonly AsyncLock _messageQueueLock = new AsyncLock();
|
||||||
|
|
||||||
private CancellationTokenSource _connectionCancellationToken;
|
CancellationTokenSource _connectionCancellationToken;
|
||||||
private CancellationTokenSource _publishingCancellationToken;
|
CancellationTokenSource _publishingCancellationToken;
|
||||||
private Task _maintainConnectionTask;
|
Task _maintainConnectionTask;
|
||||||
|
|
||||||
private ManagedMqttClientStorageManager _storageManager;
|
ManagedMqttClientStorageManager _storageManager;
|
||||||
|
|
||||||
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||||
|
InternalClient = mqttClient;
|
||||||
|
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||||
_logger = logger.CreateScopedLogger(nameof(ManagedMqttClient));
|
_logger = logger.CreateScopedLogger(nameof(ManagedMqttClient));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsConnected => _mqttClient.IsConnected;
|
public bool IsConnected => _mqttClient.IsConnected;
|
||||||
|
|
||||||
public bool IsStarted => _connectionCancellationToken != null;
|
public bool IsStarted => _connectionCancellationToken != null;
|
||||||
|
|
||||||
|
public IMqttClient InternalClient { get; }
|
||||||
|
|
||||||
public int PendingApplicationMessagesCount => _messageQueue.Count;
|
public int PendingApplicationMessagesCount => _messageQueue.Count;
|
||||||
|
|
||||||
public IManagedMqttClientOptions Options { get; private set; }
|
public IManagedMqttClientOptions Options { get; private set; }
|
||||||
|
|
||||||
public IMqttClientConnectedHandler ConnectedHandler
|
public IMqttClientConnectedHandler ConnectedHandler
|
||||||
@@ -88,8 +94,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
|
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
|
||||||
|
|
||||||
|
|
||||||
if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
|
if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
|
||||||
|
|
||||||
Options = options;
|
Options = options;
|
||||||
@@ -105,11 +110,16 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_connectionCancellationToken = new CancellationTokenSource();
|
if (Options.AutoReconnect)
|
||||||
|
{
|
||||||
_maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token);
|
var cancellationTokenSource = new CancellationTokenSource();
|
||||||
_maintainConnectionTask.Forget(_logger);
|
var cancellationToken = cancellationTokenSource.Token;
|
||||||
|
_connectionCancellationToken = cancellationTokenSource;
|
||||||
|
|
||||||
|
_maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(cancellationToken), cancellationToken);
|
||||||
|
_maintainConnectionTask.Forget(_logger);
|
||||||
|
}
|
||||||
|
|
||||||
_logger.Info("Started");
|
_logger.Info("Started");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,10 +270,11 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
_mqttClient.Dispose();
|
_mqttClient.Dispose();
|
||||||
_subscriptionsQueuedSignal.Dispose();
|
_subscriptionsQueuedSignal.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
|
async Task MaintainConnectionAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -303,7 +314,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
|
async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -346,7 +357,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
|
async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -384,7 +395,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task TryPublishQueuedMessageAsync(ManagedMqttApplicationMessage message)
|
async Task TryPublishQueuedMessageAsync(ManagedMqttApplicationMessage message)
|
||||||
{
|
{
|
||||||
Exception transmitException = null;
|
Exception transmitException = null;
|
||||||
try
|
try
|
||||||
@@ -449,7 +460,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PublishSubscriptionsAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
async Task PublishSubscriptionsAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var endTime = DateTime.UtcNow + timeout;
|
var endTime = DateTime.UtcNow + timeout;
|
||||||
while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false))
|
while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false))
|
||||||
@@ -501,7 +512,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PublishReconnectSubscriptionsAsync()
|
async Task PublishReconnectSubscriptionsAsync()
|
||||||
{
|
{
|
||||||
_logger.Info("Publishing subscriptions at reconnect");
|
_logger.Info("Publishing subscriptions at reconnect");
|
||||||
|
|
||||||
@@ -519,7 +530,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleSubscriptionExceptionAsync(Exception exception)
|
async Task HandleSubscriptionExceptionAsync(Exception exception)
|
||||||
{
|
{
|
||||||
_logger.Warning(exception, "Synchronizing subscriptions failed.");
|
_logger.Warning(exception, "Synchronizing subscriptions failed.");
|
||||||
|
|
||||||
@@ -530,7 +541,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ReconnectionResult> ReconnectIfRequiredAsync()
|
async Task<ReconnectionResult> ReconnectIfRequiredAsync()
|
||||||
{
|
{
|
||||||
if (_mqttClient.IsConnected)
|
if (_mqttClient.IsConnected)
|
||||||
{
|
{
|
||||||
@@ -554,34 +565,47 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartPublishing()
|
void StartPublishing()
|
||||||
{
|
{
|
||||||
if (_publishingCancellationToken != null)
|
if (_publishingCancellationToken != null)
|
||||||
{
|
{
|
||||||
StopPublishing();
|
StopPublishing();
|
||||||
}
|
}
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
var cancellationTokenSource = new CancellationTokenSource();
|
||||||
_publishingCancellationToken = cts;
|
var cancellationToken = cancellationTokenSource.Token;
|
||||||
|
_publishingCancellationToken = cancellationTokenSource;
|
||||||
|
|
||||||
Task.Run(() => PublishQueuedMessagesAsync(cts.Token), cts.Token).Forget(_logger);
|
Task.Run(() => PublishQueuedMessagesAsync(cancellationToken), cancellationToken).Forget(_logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StopPublishing()
|
void StopPublishing()
|
||||||
{
|
{
|
||||||
_publishingCancellationToken?.Cancel(false);
|
try
|
||||||
_publishingCancellationToken?.Dispose();
|
{
|
||||||
_publishingCancellationToken = null;
|
_publishingCancellationToken?.Cancel(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_publishingCancellationToken?.Dispose();
|
||||||
|
_publishingCancellationToken = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StopMaintainingConnection()
|
void StopMaintainingConnection()
|
||||||
{
|
{
|
||||||
_connectionCancellationToken?.Cancel(false);
|
try
|
||||||
_connectionCancellationToken?.Dispose();
|
{
|
||||||
_connectionCancellationToken = null;
|
_connectionCancellationToken?.Cancel(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_connectionCancellationToken?.Dispose();
|
||||||
|
_connectionCancellationToken = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TimeSpan GetRemainingTime(DateTime endTime)
|
static TimeSpan GetRemainingTime(DateTime endTime)
|
||||||
{
|
{
|
||||||
var remainingTime = endTime - DateTime.UtcNow;
|
var remainingTime = endTime - DateTime.UtcNow;
|
||||||
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
|
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
|
||||||
|
|||||||
@@ -76,36 +76,39 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, Func<MqttApplicationMessageReceivedEventArgs, Task> handler)
|
public static TReceiver UseApplicationMessageReceivedHandler<TReceiver>(this TReceiver receiver, Func<MqttApplicationMessageReceivedEventArgs, Task> handler)
|
||||||
|
where TReceiver : IApplicationMessageReceiver
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (receiver == null) throw new ArgumentNullException(nameof(receiver));
|
||||||
|
|
||||||
if (handler == null)
|
if (handler == null)
|
||||||
{
|
{
|
||||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
return receiver.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
return receiver.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, Action<MqttApplicationMessageReceivedEventArgs> handler)
|
public static TReceiver UseApplicationMessageReceivedHandler<TReceiver>(this TReceiver receiver, Action<MqttApplicationMessageReceivedEventArgs> handler)
|
||||||
|
where TReceiver : IApplicationMessageReceiver
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (receiver == null) throw new ArgumentNullException(nameof(receiver));
|
||||||
|
|
||||||
if (handler == null)
|
if (handler == null)
|
||||||
{
|
{
|
||||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
return receiver.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
return receiver.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, IMqttApplicationMessageReceivedHandler handler)
|
public static TReceiver UseApplicationMessageReceivedHandler<TReceiver>(this TReceiver receiver, IMqttApplicationMessageReceivedHandler handler)
|
||||||
|
where TReceiver : IApplicationMessageReceiver
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (receiver == null) throw new ArgumentNullException(nameof(receiver));
|
||||||
|
|
||||||
client.ApplicationMessageReceivedHandler = handler;
|
receiver.ApplicationMessageReceivedHandler = handler;
|
||||||
return client;
|
return receiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task SubscribeAsync(this IManagedMqttClient client, params MqttTopicFilter[] topicFilters)
|
public static Task SubscribeAsync(this IManagedMqttClient client, params MqttTopicFilter[] topicFilters)
|
||||||
@@ -138,92 +141,92 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
return client.UnsubscribeAsync(topicFilters);
|
return client.UnsubscribeAsync(topicFilters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task PublishAsync(this IManagedMqttClient client, IEnumerable<MqttApplicationMessage> applicationMessages)
|
public static async Task PublishAsync(this IApplicationMessagePublisher publisher, IEnumerable<MqttApplicationMessage> applicationMessages)
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||||
|
|
||||||
foreach (var applicationMessage in applicationMessages)
|
foreach (var applicationMessage in applicationMessages)
|
||||||
{
|
{
|
||||||
await client.PublishAsync(applicationMessage).ConfigureAwait(false);
|
await publisher.PublishAsync(applicationMessage).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, MqttApplicationMessage applicationMessage)
|
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, MqttApplicationMessage applicationMessage)
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
|
||||||
return client.PublishAsync(applicationMessage, CancellationToken.None);
|
return publisher.PublishAsync(applicationMessage, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task PublishAsync(this IManagedMqttClient client, params MqttApplicationMessage[] applicationMessages)
|
public static async Task PublishAsync(this IApplicationMessagePublisher publisher, params MqttApplicationMessage[] applicationMessages)
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||||
|
|
||||||
foreach (var applicationMessage in applicationMessages)
|
foreach (var applicationMessage in applicationMessages)
|
||||||
{
|
{
|
||||||
await client.PublishAsync(applicationMessage, CancellationToken.None).ConfigureAwait(false);
|
await publisher.PublishAsync(applicationMessage, CancellationToken.None).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic)
|
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, string topic)
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||||
|
|
||||||
return client.PublishAsync(builder => builder
|
return publisher.PublishAsync(builder => builder
|
||||||
.WithTopic(topic));
|
.WithTopic(topic));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload)
|
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload)
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||||
|
|
||||||
return client.PublishAsync(builder => builder
|
return publisher.PublishAsync(builder => builder
|
||||||
.WithTopic(topic)
|
.WithTopic(topic)
|
||||||
.WithPayload(payload));
|
.WithPayload(payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||||
|
|
||||||
return client.PublishAsync(builder => builder
|
return publisher.PublishAsync(builder => builder
|
||||||
.WithTopic(topic)
|
.WithTopic(topic)
|
||||||
.WithPayload(payload)
|
.WithPayload(payload)
|
||||||
.WithQualityOfServiceLevel(qualityOfServiceLevel));
|
.WithQualityOfServiceLevel(qualityOfServiceLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||||
|
|
||||||
return client.PublishAsync(builder => builder
|
return publisher.PublishAsync(builder => builder
|
||||||
.WithTopic(topic)
|
.WithTopic(topic)
|
||||||
.WithPayload(payload)
|
.WithPayload(payload)
|
||||||
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
||||||
.WithRetainFlag(retain));
|
.WithRetainFlag(retain));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder, CancellationToken cancellationToken)
|
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||||
|
|
||||||
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
||||||
return client.PublishAsync(message, cancellationToken);
|
return publisher.PublishAsync(message, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
|
public static Task<MqttClientPublishResult> PublishAsync(this IApplicationMessagePublisher publisher, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
|
||||||
{
|
{
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||||
|
|
||||||
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
||||||
return client.PublishAsync(message, CancellationToken.None);
|
return publisher.PublishAsync(message, CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Client.Options;
|
using MQTTnet.Client.Options;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
@@ -9,6 +8,8 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
public IMqttClientOptions ClientOptions { get; set; }
|
public IMqttClientOptions ClientOptions { get; set; }
|
||||||
|
|
||||||
|
public bool AutoReconnect { get; set; } = true;
|
||||||
|
|
||||||
public TimeSpan AutoReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
|
public TimeSpan AutoReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
public TimeSpan ConnectionCheckInterval { get; set; } = TimeSpan.FromSeconds(1);
|
public TimeSpan ConnectionCheckInterval { get; set; } = TimeSpan.FromSeconds(1);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Client.Options;
|
using MQTTnet.Client.Options;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
@@ -7,8 +6,8 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
public class ManagedMqttClientOptionsBuilder
|
public class ManagedMqttClientOptionsBuilder
|
||||||
{
|
{
|
||||||
private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
|
readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
|
||||||
private MqttClientOptionsBuilder _clientOptionsBuilder;
|
MqttClientOptionsBuilder _clientOptionsBuilder;
|
||||||
|
|
||||||
public ManagedMqttClientOptionsBuilder WithMaxPendingMessages(int value)
|
public ManagedMqttClientOptionsBuilder WithMaxPendingMessages(int value)
|
||||||
{
|
{
|
||||||
@@ -70,6 +69,18 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ManagedMqttClientOptionsBuilder WithAutoReconnect()
|
||||||
|
{
|
||||||
|
_options.AutoReconnect = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManagedMqttClientOptionsBuilder WithoutAutoReconnect()
|
||||||
|
{
|
||||||
|
_options.AutoReconnect = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ManagedMqttClientOptions Build()
|
public ManagedMqttClientOptions Build()
|
||||||
{
|
{
|
||||||
if (_clientOptionsBuilder != null)
|
if (_clientOptionsBuilder != null)
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
<Company />
|
<Company />
|
||||||
<Authors />
|
<Authors />
|
||||||
<PackageId />
|
<PackageId />
|
||||||
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
||||||
@@ -23,6 +26,10 @@
|
|||||||
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
|
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
+2
-3
@@ -1,7 +1,6 @@
|
|||||||
using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
|
using System;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Extensions.Rpc.Options
|
namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
|
||||||
{
|
{
|
||||||
public class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
|
public class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
<Company />
|
<Company />
|
||||||
<Authors />
|
<Authors />
|
||||||
<PackageId />
|
<PackageId />
|
||||||
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
||||||
@@ -23,6 +26,10 @@
|
|||||||
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
|
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
|
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"dotnet-ef": {
|
||||||
|
"version": "3.1.4",
|
||||||
|
"commands": [
|
||||||
|
"dotnet-ef"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,19 +8,22 @@
|
|||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
<Company />
|
<Company />
|
||||||
<Product>MQTTnet</Product>
|
<Product>MQTTnet</Product>
|
||||||
<Description />
|
<Description>Standalone MQTT server with Python scripting backend.</Description>
|
||||||
<Authors />
|
<Authors>The authors of MQTTnet.Server.</Authors>
|
||||||
<PackageId />
|
<PackageId />
|
||||||
<SignAssembly>false</SignAssembly>
|
<SignAssembly>false</SignAssembly>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<Version>1.0.0</Version>
|
<Version>3.0.11</Version>
|
||||||
<TypeScriptToolsVersion>3.3</TypeScriptToolsVersion>
|
<TypeScriptToolsVersion>3.3</TypeScriptToolsVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<StartupObject>MQTTnet.Server.Program</StartupObject>
|
<StartupObject>MQTTnet.Server.Program</StartupObject>
|
||||||
<UserSecretsId>c564f0de-28b4-45bf-b726-4d665d705653</UserSecretsId>
|
<UserSecretsId>c564f0de-28b4-45bf-b726-4d665d705653</UserSecretsId>
|
||||||
|
<Copyright>(c) Christian Kratky 2016-2020</Copyright>
|
||||||
|
<PackageProjectUrl>https://github.com/chkr1011/MQTTnet</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://github.com/chkr1011/MQTTnet</RepositoryUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
@@ -36,12 +39,12 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="IronPython" Version="2.7.10-candidate1" />
|
<PackageReference Include="IronPython" Version="2.7.10" />
|
||||||
<PackageReference Include="IronPython.StdLib" Version="2.7.10-candidate1" />
|
<PackageReference Include="IronPython.StdLib" Version="2.7.10" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.5" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.3.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.5.1" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.3.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ namespace MQTTnet.Adapter
|
|||||||
{
|
{
|
||||||
public sealed class MqttChannelAdapter : Disposable, IMqttChannelAdapter
|
public sealed class MqttChannelAdapter : Disposable, IMqttChannelAdapter
|
||||||
{
|
{
|
||||||
const uint _errorOperationAborted = 0x800703E3;
|
const uint ErrorOperationAborted = 0x800703E3;
|
||||||
const int _readBufferSize = 4096; // TODO: Move buffer size to config
|
const int ReadBufferSize = 4096;
|
||||||
|
|
||||||
readonly IMqttNetScopedLogger _logger;
|
readonly IMqttNetScopedLogger _logger;
|
||||||
readonly IMqttChannel _channel;
|
readonly IMqttChannel _channel;
|
||||||
@@ -25,7 +25,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||||
|
|
||||||
readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
|
readonly AsyncLock _syncRoot = new AsyncLock();
|
||||||
|
|
||||||
long _bytesReceived;
|
long _bytesReceived;
|
||||||
long _bytesSent;
|
long _bytesSent;
|
||||||
@@ -58,6 +58,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -84,6 +85,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
public async Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
public async Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -111,45 +113,58 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var packetData = PacketFormatterAdapter.Encode(packet);
|
using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false))
|
||||||
|
|
||||||
if (timeout == TimeSpan.Zero)
|
|
||||||
{
|
{
|
||||||
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
|
// Check for cancellation here again because "WaitAsync" might take some time.
|
||||||
}
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
else
|
|
||||||
{
|
|
||||||
await MqttTaskTimeout.WaitAsync(
|
|
||||||
t => _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, t), timeout, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Interlocked.Add(ref _bytesReceived, packetData.Count);
|
try
|
||||||
|
{
|
||||||
|
var packetData = PacketFormatterAdapter.Encode(packet);
|
||||||
|
|
||||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
|
if (timeout == TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await MqttTaskTimeout.WaitAsync(
|
||||||
|
t => _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, t), timeout, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Interlocked.Add(ref _bytesReceived, packetData.Count);
|
||||||
|
|
||||||
|
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
if (IsWrappedException(exception))
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapException(exception);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
PacketFormatterAdapter.FreeBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (ObjectDisposedException)
|
||||||
{
|
{
|
||||||
if (IsWrappedException(exception))
|
throw new OperationCanceledException();
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
WrapException(exception);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
PacketFormatterAdapter.FreeBuffer();
|
|
||||||
_writerSemaphore?.Release();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -189,6 +204,9 @@ namespace MQTTnet.Adapter
|
|||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
if (IsWrappedException(exception))
|
if (IsWrappedException(exception))
|
||||||
@@ -212,8 +230,8 @@ namespace MQTTnet.Adapter
|
|||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
_channel?.Dispose();
|
_channel.Dispose();
|
||||||
_writerSemaphore?.Dispose();
|
_syncRoot.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
@@ -245,7 +263,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
var body = new byte[fixedHeader.RemainingLength];
|
var body = new byte[fixedHeader.RemainingLength];
|
||||||
var bodyOffset = 0;
|
var bodyOffset = 0;
|
||||||
var chunkSize = Math.Min(_readBufferSize, fixedHeader.RemainingLength);
|
var chunkSize = Math.Min(ReadBufferSize, fixedHeader.RemainingLength);
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@@ -304,7 +322,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
if (exception is COMException comException)
|
if (exception is COMException comException)
|
||||||
{
|
{
|
||||||
if ((uint)comException.HResult == _errorOperationAborted)
|
if ((uint)comException.HResult == ErrorOperationAborted)
|
||||||
{
|
{
|
||||||
throw new OperationCanceledException();
|
throw new OperationCanceledException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
namespace MQTTnet.Certificates
|
||||||
|
{
|
||||||
|
public class BlobCertificateProvider : ICertificateProvider
|
||||||
|
{
|
||||||
|
public BlobCertificateProvider(byte[] blob)
|
||||||
|
{
|
||||||
|
Blob = blob ?? throw new ArgumentNullException(nameof(blob));
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Blob { get; }
|
||||||
|
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
public X509Certificate2 GetCertificate()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Password))
|
||||||
|
{
|
||||||
|
// Use a different overload when no password is specified. Otherwise the constructor will fail.
|
||||||
|
return new X509Certificate2(Blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new X509Certificate2(Blob, Password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
namespace MQTTnet.Certificates
|
||||||
|
{
|
||||||
|
public interface ICertificateProvider
|
||||||
|
{
|
||||||
|
X509Certificate2 GetCertificate();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#if !WINDOWS_UWP
|
||||||
|
using System;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
namespace MQTTnet.Certificates
|
||||||
|
{
|
||||||
|
public class X509CertificateProvider : ICertificateProvider
|
||||||
|
{
|
||||||
|
readonly X509Certificate2 _certificate;
|
||||||
|
|
||||||
|
public X509CertificateProvider(X509Certificate2 certificate)
|
||||||
|
{
|
||||||
|
_certificate = certificate ?? throw new ArgumentNullException(nameof(certificate));
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate2 GetCertificate()
|
||||||
|
{
|
||||||
|
return _certificate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -3,6 +3,34 @@
|
|||||||
public enum MqttClientDisconnectReason
|
public enum MqttClientDisconnectReason
|
||||||
{
|
{
|
||||||
NormalDisconnection = 0,
|
NormalDisconnection = 0,
|
||||||
|
DisconnectWithWillMessage = 4,
|
||||||
|
UnspecifiedError = 128,
|
||||||
|
MalformedPacket = 129,
|
||||||
|
ProtocolError = 130,
|
||||||
|
ImplementationSpecificError = 131,
|
||||||
|
NotAuthorized = 135,
|
||||||
|
ServerBusy = 137,
|
||||||
|
ServerShuttingDown = 139,
|
||||||
|
BadAuthenticationMethod = 140,
|
||||||
|
KeepaliveTimeout = 141,
|
||||||
|
SessionTakenOver = 142,
|
||||||
|
TopicFilterInvalid = 143,
|
||||||
|
TopicNameInvalid = 144,
|
||||||
|
ReceiveMaximumExceeded = 147,
|
||||||
|
TopicAliasInvalid = 148,
|
||||||
|
PacketTooLarge = 149,
|
||||||
|
MessageRateTooHigh = 150,
|
||||||
|
QuotaExceeded = 151,
|
||||||
|
AdministrativeAction = 152,
|
||||||
|
PayloadFormatInvalid = 153,
|
||||||
|
RetainNotSupported = 154,
|
||||||
|
QosNotSupported = 155,
|
||||||
|
UseAnotherServer = 156,
|
||||||
|
ServerMoved = 157,
|
||||||
|
SharedSubscriptionsNotSupported = 158,
|
||||||
|
ConnectionRateExceeded = 159,
|
||||||
|
MaximumConnectTime = 160,
|
||||||
|
SubscriptionIdentifiersNotSupported = 161,
|
||||||
|
WildcardSubscriptionsNotSupported = 162
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ namespace MQTTnet.Client.Disconnecting
|
|||||||
{
|
{
|
||||||
public class MqttClientDisconnectedEventArgs : EventArgs
|
public class MqttClientDisconnectedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientAuthenticateResult authenticateResult)
|
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientAuthenticateResult authenticateResult, MqttClientDisconnectReason reasonCode)
|
||||||
{
|
{
|
||||||
ClientWasConnected = clientWasConnected;
|
ClientWasConnected = clientWasConnected;
|
||||||
Exception = exception;
|
Exception = exception;
|
||||||
AuthenticateResult = authenticateResult;
|
AuthenticateResult = authenticateResult;
|
||||||
|
ReasonCode = reasonCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ClientWasConnected { get; }
|
public bool ClientWasConnected { get; }
|
||||||
@@ -17,5 +18,7 @@ namespace MQTTnet.Client.Disconnecting
|
|||||||
public Exception Exception { get; }
|
public Exception Exception { get; }
|
||||||
|
|
||||||
public MqttClientAuthenticateResult AuthenticateResult { get; }
|
public MqttClientAuthenticateResult AuthenticateResult { get; }
|
||||||
|
|
||||||
|
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using MQTTnet.Adapter;
|
using System;
|
||||||
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
|
using MQTTnet.LowLevelClient;
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
namespace MQTTnet.Client
|
||||||
{
|
{
|
||||||
@@ -7,6 +9,14 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
IMqttFactory UseClientAdapterFactory(IMqttClientAdapterFactory clientAdapterFactory);
|
IMqttFactory UseClientAdapterFactory(IMqttClientAdapterFactory clientAdapterFactory);
|
||||||
|
|
||||||
|
ILowLevelMqttClient CreateLowLevelMqttClient();
|
||||||
|
|
||||||
|
ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger);
|
||||||
|
|
||||||
|
ILowLevelMqttClient CreateLowLevelMqttClient(IMqttClientAdapterFactory clientAdapterFactory);
|
||||||
|
|
||||||
|
ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory clientAdapterFactory);
|
||||||
|
|
||||||
IMqttClient CreateMqttClient();
|
IMqttClient CreateMqttClient();
|
||||||
|
|
||||||
IMqttClient CreateMqttClient(IMqttNetLogger logger);
|
IMqttClient CreateMqttClient(IMqttNetLogger logger);
|
||||||
|
|||||||
@@ -57,13 +57,7 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; }
|
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; }
|
||||||
|
|
||||||
public bool IsConnected
|
public bool IsConnected => _isConnected && Interlocked.Read(ref _isDisconnectPending) == 0;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _isConnected && Interlocked.Read(ref _isDisconnectPending) == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMqttClientOptions Options { get; private set; }
|
public IMqttClientOptions Options { get; private set; }
|
||||||
|
|
||||||
@@ -164,9 +158,9 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PingAsync(CancellationToken cancellationToken)
|
public Task PingAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket(), cancellationToken).ConfigureAwait(false);
|
return SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket(), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken)
|
public Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken)
|
||||||
@@ -251,17 +245,22 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
void Cleanup()
|
void Cleanup()
|
||||||
{
|
{
|
||||||
_backgroundCancellationTokenSource?.Cancel(false);
|
try
|
||||||
_backgroundCancellationTokenSource?.Dispose();
|
{
|
||||||
_backgroundCancellationTokenSource = null;
|
_backgroundCancellationTokenSource?.Cancel(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_backgroundCancellationTokenSource?.Dispose();
|
||||||
|
_backgroundCancellationTokenSource = null;
|
||||||
|
|
||||||
_publishPacketReceiverQueue?.Dispose();
|
_publishPacketReceiverQueue?.Dispose();
|
||||||
_publishPacketReceiverQueue = null;
|
_publishPacketReceiverQueue = null;
|
||||||
|
|
||||||
_adapter?.Dispose();
|
_adapter?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
@@ -308,7 +307,8 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
async Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult)
|
async Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult)
|
||||||
{
|
{
|
||||||
var clientWasConnected = IsConnected;
|
var clientWasConnected = _isConnected;
|
||||||
|
var reasonCode = MqttClientDisconnectReason.NormalDisconnection;
|
||||||
|
|
||||||
TryInitiateDisconnect();
|
TryInitiateDisconnect();
|
||||||
_isConnected = false;
|
_isConnected = false;
|
||||||
@@ -326,6 +326,7 @@ namespace MQTTnet.Client
|
|||||||
catch (Exception adapterException)
|
catch (Exception adapterException)
|
||||||
{
|
{
|
||||||
_logger.Warning(adapterException, "Error while disconnecting from adapter.");
|
_logger.Warning(adapterException, "Error while disconnecting from adapter.");
|
||||||
|
reasonCode = MqttClientDisconnectReason.UnspecifiedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -341,6 +342,7 @@ namespace MQTTnet.Client
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.Warning(e, "Error while waiting for internal tasks.");
|
_logger.Warning(e, "Error while waiting for internal tasks.");
|
||||||
|
reasonCode = MqttClientDisconnectReason.UnspecifiedError;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -354,7 +356,7 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
// This handler must be executed in a new thread because otherwise a dead lock may happen
|
// This handler must be executed in a new thread because otherwise a dead lock may happen
|
||||||
// when trying to reconnect in that handler etc.
|
// when trying to reconnect in that handler etc.
|
||||||
Task.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, authenticateResult))).Forget(_logger);
|
Task.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, authenticateResult, reasonCode))).Forget(_logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,7 +393,7 @@ namespace MQTTnet.Client
|
|||||||
async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttBasePacket
|
async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttBasePacket
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
ushort identifier = 0;
|
ushort identifier = 0;
|
||||||
if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier.HasValue)
|
if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier.HasValue)
|
||||||
{
|
{
|
||||||
@@ -440,7 +442,7 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
// Values described here: [MQTT-3.1.2-24].
|
// Values described here: [MQTT-3.1.2-24].
|
||||||
var waitTime = keepAlivePeriod - _sendTracker.Elapsed;
|
var waitTime = keepAlivePeriod - _sendTracker.Elapsed;
|
||||||
|
|
||||||
if (waitTime <= TimeSpan.Zero)
|
if (waitTime <= TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket(), cancellationToken).ConfigureAwait(false);
|
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket(), cancellationToken).ConfigureAwait(false);
|
||||||
@@ -565,12 +567,15 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
await SendAsync(new MqttPingRespPacket(), cancellationToken).ConfigureAwait(false);
|
await SendAsync(new MqttPingRespPacket(), cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else if (packet is MqttDisconnectPacket)
|
else if (packet is MqttDisconnectPacket disconnectPacket)
|
||||||
{
|
{
|
||||||
// Also dispatch disconnect to waiting threads to generate a proper exception.
|
// Also dispatch disconnect to waiting threads to generate a proper exception.
|
||||||
_packetDispatcher.Dispatch(packet);
|
_packetDispatcher.Dispatch(packet);
|
||||||
|
|
||||||
await DisconnectAsync(null, cancellationToken).ConfigureAwait(false);
|
await DisconnectAsync(new MqttClientDisconnectOptions()
|
||||||
|
{
|
||||||
|
ReasonCode = (MqttClientDisconnectReason)(disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.UnspecifiedError)
|
||||||
|
}, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else if (packet is MqttAuthPacket authPacket)
|
else if (packet is MqttAuthPacket authPacket)
|
||||||
{
|
{
|
||||||
@@ -632,7 +637,6 @@ namespace MQTTnet.Client
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var publishPacketDequeueResult = await _publishPacketReceiverQueue.TryDequeueAsync(cancellationToken);
|
var publishPacketDequeueResult = await _publishPacketReceiverQueue.TryDequeueAsync(cancellationToken);
|
||||||
|
|
||||||
if (!publishPacketDequeueResult.IsSuccess)
|
if (!publishPacketDequeueResult.IsSuccess)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -280,7 +280,9 @@ namespace MQTTnet.Client.Options
|
|||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
|
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
|
#if NETCOREAPP3_1
|
||||||
|
ApplicationProtocols = _tlsParameters.ApplicationProtocols,
|
||||||
|
#endif
|
||||||
CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
|
CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
|
||||||
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
|
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
|
||||||
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors
|
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ namespace MQTTnet.Client.Options
|
|||||||
public IEnumerable<X509Certificate> Certificates { get; set; }
|
public IEnumerable<X509Certificate> Certificates { get; set; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public bool AllowUntrustedCertificates { get; set; }
|
#if NETCOREAPP3_1
|
||||||
|
public List<SslApplicationProtocol> ApplicationProtocols { get;set; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public bool AllowUntrustedCertificates { get; set; }
|
||||||
|
|
||||||
public bool IgnoreCertificateChainErrors { get; set; }
|
public bool IgnoreCertificateChainErrors { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ namespace MQTTnet.Client.Options
|
|||||||
public List<X509Certificate> Certificates { get; set; }
|
public List<X509Certificate> Certificates { get; set; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if NETCOREAPP3_1
|
||||||
|
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
|
||||||
|
#endif
|
||||||
|
|
||||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
||||||
|
|
||||||
[Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
|
[Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
return "netstandard2.1";
|
return "netstandard2.1";
|
||||||
#elif WINDOWS_UWP
|
#elif WINDOWS_UWP
|
||||||
return "uap10.0";
|
return "uap10.0";
|
||||||
|
#elif NETCOREAPP3_1
|
||||||
|
return "netcoreapp3.1";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ namespace MQTTnet.Formatter
|
|||||||
{
|
{
|
||||||
if (_length < _offset + length)
|
if (_length < _offset + length)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(_buffer), $"Expected at least {_offset + length} bytes but there are only {_length} bytes");
|
throw new MqttProtocolViolationException($"Expected at least {_offset + length} bytes but there are only {_length} bytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,6 +120,14 @@ namespace MQTTnet.Formatter
|
|||||||
{
|
{
|
||||||
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
||||||
|
|
||||||
|
if (receivedMqttPacket.Body.Length < 7)
|
||||||
|
{
|
||||||
|
// 2 byte protocol name length
|
||||||
|
// at least 4 byte protocol name
|
||||||
|
// 1 byte protocol level
|
||||||
|
throw new MqttProtocolViolationException("CONNECT packet must have at least 7 bytes.");
|
||||||
|
}
|
||||||
|
|
||||||
var protocolName = receivedMqttPacket.Body.ReadStringWithLengthPrefix();
|
var protocolName = receivedMqttPacket.Body.ReadStringWithLengthPrefix();
|
||||||
var protocolLevel = receivedMqttPacket.Body.ReadByte();
|
var protocolLevel = receivedMqttPacket.Body.ReadByte();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MQTTnet.Client;
|
using System.Collections.Generic;
|
||||||
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
@@ -7,5 +8,7 @@ namespace MQTTnet
|
|||||||
public interface IMqttFactory : IMqttClientFactory, IMqttServerFactory
|
public interface IMqttFactory : IMqttClientFactory, IMqttServerFactory
|
||||||
{
|
{
|
||||||
IMqttNetLogger DefaultLogger { get; }
|
IMqttNetLogger DefaultLogger { get; }
|
||||||
|
|
||||||
|
IDictionary<object, object> Properties { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
public CrossPlatformSocket()
|
public CrossPlatformSocket()
|
||||||
{
|
{
|
||||||
// Having this contructor is important because avoiding the address family as parameter
|
// Having this constructor is important because avoiding the address family as parameter
|
||||||
// will make use of dual mode in the .net framework.
|
// will make use of dual mode in the .net framework.
|
||||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||||
}
|
}
|
||||||
@@ -33,75 +33,34 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
public bool NoDelay
|
public bool NoDelay
|
||||||
{
|
{
|
||||||
get
|
get => (int)_socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay) > 0;
|
||||||
{
|
set => _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
|
||||||
return (int)_socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DualMode
|
public bool DualMode
|
||||||
{
|
{
|
||||||
get
|
get => _socket.DualMode;
|
||||||
{
|
set => _socket.DualMode = value;
|
||||||
return _socket.DualMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_socket.DualMode = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ReceiveBufferSize
|
public int ReceiveBufferSize
|
||||||
{
|
{
|
||||||
get
|
get => _socket.ReceiveBufferSize;
|
||||||
{
|
set => _socket.ReceiveBufferSize = value;
|
||||||
return _socket.ReceiveBufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_socket.ReceiveBufferSize = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int SendBufferSize
|
public int SendBufferSize
|
||||||
{
|
{
|
||||||
get
|
get => _socket.SendBufferSize;
|
||||||
{
|
set => _socket.SendBufferSize = value;
|
||||||
return _socket.SendBufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_socket.SendBufferSize = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EndPoint RemoteEndPoint
|
public EndPoint RemoteEndPoint => _socket.RemoteEndPoint;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _socket.RemoteEndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ReuseAddress
|
public bool ReuseAddress
|
||||||
{
|
{
|
||||||
get
|
get => (int)_socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress) != 0;
|
||||||
{
|
set => _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, value ? 1 : 0);
|
||||||
return (int)_socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, value ? 1 : 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CrossPlatformSocket> AcceptAsync()
|
public async Task<CrossPlatformSocket> AcceptAsync()
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ using MQTTnet.Server;
|
|||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
public class MqttTcpChannel : IMqttChannel
|
public sealed class MqttTcpChannel : IMqttChannel
|
||||||
{
|
{
|
||||||
private readonly MqttClientTcpOptions _options;
|
readonly MqttClientTcpOptions _options;
|
||||||
private readonly int _bufferSize;
|
readonly int _bufferSize;
|
||||||
|
|
||||||
private StreamSocket _socket;
|
StreamSocket _socket;
|
||||||
private Stream _readStream;
|
Stream _readStream;
|
||||||
private Stream _writeStream;
|
Stream _writeStream;
|
||||||
|
|
||||||
public MqttTcpChannel(IMqttClientOptions clientOptions)
|
public MqttTcpChannel(IMqttClientOptions clientOptions)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
public sealed class MqttTcpChannel : IDisposable, IMqttChannel
|
public sealed class MqttTcpChannel : IMqttChannel
|
||||||
{
|
{
|
||||||
readonly IMqttClientOptions _clientOptions;
|
readonly IMqttClientOptions _clientOptions;
|
||||||
readonly MqttClientTcpOptions _options;
|
readonly MqttClientTcpOptions _options;
|
||||||
@@ -81,11 +81,24 @@ namespace MQTTnet.Implementations
|
|||||||
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
#if NETCOREAPP3_1
|
||||||
|
var sslOptions = new SslClientAuthenticationOptions
|
||||||
|
{
|
||||||
|
ApplicationProtocols = _options.TlsOptions.ApplicationProtocols,
|
||||||
|
ClientCertificates = LoadCertificates(),
|
||||||
|
EnabledSslProtocols = _options.TlsOptions.SslProtocol,
|
||||||
|
CertificateRevocationCheckMode = _options.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
|
||||||
|
TargetHost = _options.Server
|
||||||
|
};
|
||||||
|
|
||||||
|
await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
#else
|
||||||
await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, !_options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, !_options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
#if NETSTANDARD2_1
|
#if NETSTANDARD2_1 || NETCOREAPP3_1
|
||||||
await sslStream.DisposeAsync().ConfigureAwait(false);
|
await sslStream.DisposeAsync().ConfigureAwait(false);
|
||||||
#else
|
#else
|
||||||
sslStream.Dispose();
|
sslStream.Dispose();
|
||||||
@@ -127,7 +140,14 @@ namespace MQTTnet.Implementations
|
|||||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||||
using (cancellationToken.Register(Dispose))
|
using (cancellationToken.Register(Dispose))
|
||||||
{
|
{
|
||||||
return await _stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
var stream = _stream;
|
||||||
|
|
||||||
|
if (stream == null)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
return await stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException)
|
catch (ObjectDisposedException)
|
||||||
@@ -157,12 +177,18 @@ namespace MQTTnet.Implementations
|
|||||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||||
using (cancellationToken.Register(Dispose))
|
using (cancellationToken.Register(Dispose))
|
||||||
{
|
{
|
||||||
await _stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
var stream = _stream;
|
||||||
|
|
||||||
|
if (stream == null)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException)
|
catch (ObjectDisposedException)
|
||||||
{
|
{
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
catch (IOException exception)
|
catch (IOException exception)
|
||||||
{
|
{
|
||||||
@@ -196,7 +222,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||||
{
|
{
|
||||||
#region OBSOLETE
|
#region OBSOLETE
|
||||||
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
var certificateValidationCallback = _options?.TlsOptions?.CertificateValidationCallback;
|
var certificateValidationCallback = _options?.TlsOptions?.CertificateValidationCallback;
|
||||||
@@ -205,8 +231,7 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
return certificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions);
|
return certificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions);
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
#endregion
|
|
||||||
|
|
||||||
var certificateValidationHandler = _options?.TlsOptions?.CertificateValidationHandler;
|
var certificateValidationHandler = _options?.TlsOptions?.CertificateValidationHandler;
|
||||||
if (certificateValidationHandler != null)
|
if (certificateValidationHandler != null)
|
||||||
@@ -229,7 +254,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.OfflineRevocation))
|
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.OfflineRevocation))
|
||||||
{
|
{
|
||||||
if (!_options.TlsOptions.IgnoreCertificateRevocationErrors)
|
if (_options?.TlsOptions?.IgnoreCertificateRevocationErrors != true)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -237,13 +262,13 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain))
|
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain))
|
||||||
{
|
{
|
||||||
if (!_options.TlsOptions.IgnoreCertificateChainErrors)
|
if (_options?.TlsOptions?.IgnoreCertificateChainErrors != true)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _options.TlsOptions.AllowUntrustedCertificates;
|
return _options?.TlsOptions?.AllowUntrustedCertificates == true;
|
||||||
}
|
}
|
||||||
|
|
||||||
X509CertificateCollection LoadCertificates()
|
X509CertificateCollection LoadCertificates()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#if !WINDOWS_UWP
|
#if !WINDOWS_UWP
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -13,7 +12,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
public sealed class MqttTcpServerAdapter : Disposable, IMqttServerAdapter
|
public sealed class MqttTcpServerAdapter : IMqttServerAdapter
|
||||||
{
|
{
|
||||||
readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>();
|
readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>();
|
||||||
readonly IMqttNetScopedLogger _logger;
|
readonly IMqttNetScopedLogger _logger;
|
||||||
@@ -44,22 +43,12 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
if (options.TlsEndpointOptions?.IsEnabled == true)
|
if (options.TlsEndpointOptions?.IsEnabled == true)
|
||||||
{
|
{
|
||||||
if (options.TlsEndpointOptions.Certificate == null)
|
if (options.TlsEndpointOptions.CertificateProvider == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("TLS certificate is not set.");
|
throw new ArgumentException("TLS certificate is not set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
X509Certificate2 tlsCertificate;
|
var tlsCertificate = options.TlsEndpointOptions.CertificateProvider.GetCertificate();
|
||||||
if (string.IsNullOrEmpty(options.TlsEndpointOptions.CertificateCredentials?.Password))
|
|
||||||
{
|
|
||||||
// Use a different overload when no password is specified. Otherwise the constructor will fail.
|
|
||||||
tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate, options.TlsEndpointOptions.CertificateCredentials.Password);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tlsCertificate.HasPrivateKey)
|
if (!tlsCertificate.HasPrivateKey)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("The certificate for TLS encryption must contain the private key.");
|
throw new InvalidOperationException("The certificate for TLS encryption must contain the private key.");
|
||||||
@@ -68,23 +57,18 @@ namespace MQTTnet.Implementations
|
|||||||
RegisterListeners(options.TlsEndpointOptions, tlsCertificate, _cancellationTokenSource.Token);
|
RegisterListeners(options.TlsEndpointOptions, tlsCertificate, _cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(0);
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopAsync()
|
public Task StopAsync()
|
||||||
{
|
{
|
||||||
Cleanup();
|
Cleanup();
|
||||||
return Task.FromResult(0);
|
return PlatformAbstractionLayer.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (disposing)
|
Cleanup();
|
||||||
{
|
|
||||||
Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cleanup()
|
void Cleanup()
|
||||||
@@ -130,7 +114,7 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task OnClientAcceptedAsync(IMqttChannelAdapter channelAdapter)
|
Task OnClientAcceptedAsync(IMqttChannelAdapter channelAdapter)
|
||||||
{
|
{
|
||||||
var clientHandler = ClientHandler;
|
var clientHandler = ClientHandler;
|
||||||
if (clientHandler == null)
|
if (clientHandler == null)
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ namespace MQTTnet.Implementations
|
|||||||
readonly MqttServerTlsTcpEndpointOptions _tlsOptions;
|
readonly MqttServerTlsTcpEndpointOptions _tlsOptions;
|
||||||
readonly X509Certificate2 _tlsCertificate;
|
readonly X509Certificate2 _tlsCertificate;
|
||||||
|
|
||||||
private CrossPlatformSocket _socket;
|
CrossPlatformSocket _socket;
|
||||||
private IPEndPoint _localEndPoint;
|
IPEndPoint _localEndPoint;
|
||||||
|
|
||||||
public MqttTcpServerListener(
|
public MqttTcpServerListener(
|
||||||
AddressFamily addressFamily,
|
AddressFamily addressFamily,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
public bool IsSecureConnection { get; private set; }
|
public bool IsSecureConnection { get; private set; }
|
||||||
|
|
||||||
public X509Certificate2 ClientCertificate { get; private set; }
|
public X509Certificate2 ClientCertificate { get; }
|
||||||
|
|
||||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ using System.Threading.Tasks;
|
|||||||
namespace MQTTnet.Internal
|
namespace MQTTnet.Internal
|
||||||
{
|
{
|
||||||
// From Stephen Toub (https://blogs.msdn.microsoft.com/pfxteam/2012/02/12/building-async-coordination-primitives-part-6-asynclock/)
|
// From Stephen Toub (https://blogs.msdn.microsoft.com/pfxteam/2012/02/12/building-async-coordination-primitives-part-6-asynclock/)
|
||||||
public class AsyncLock : IDisposable
|
public sealed class AsyncLock : IDisposable
|
||||||
{
|
{
|
||||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
||||||
private readonly Task<IDisposable> _releaser;
|
readonly Task<IDisposable> _releaser;
|
||||||
|
|
||||||
public AsyncLock()
|
public AsyncLock()
|
||||||
{
|
{
|
||||||
@@ -39,9 +39,9 @@ namespace MQTTnet.Internal
|
|||||||
_semaphore?.Dispose();
|
_semaphore?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Releaser : IDisposable
|
class Releaser : IDisposable
|
||||||
{
|
{
|
||||||
private readonly AsyncLock _toRelease;
|
readonly AsyncLock _toRelease;
|
||||||
|
|
||||||
internal Releaser(AsyncLock toRelease)
|
internal Releaser(AsyncLock toRelease)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,16 +7,15 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
public sealed class AsyncQueue<TItem> : IDisposable
|
public sealed class AsyncQueue<TItem> : IDisposable
|
||||||
{
|
{
|
||||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0);
|
SemaphoreSlim _semaphore = new SemaphoreSlim(0);
|
||||||
|
ConcurrentQueue<TItem> _queue = new ConcurrentQueue<TItem>();
|
||||||
private ConcurrentQueue<TItem> _queue = new ConcurrentQueue<TItem>();
|
|
||||||
|
|
||||||
public int Count => _queue.Count;
|
public int Count => _queue.Count;
|
||||||
|
|
||||||
public void Enqueue(TItem item)
|
public void Enqueue(TItem item)
|
||||||
{
|
{
|
||||||
_queue.Enqueue(item);
|
_queue.Enqueue(item);
|
||||||
_semaphore.Release();
|
_semaphore?.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AsyncQueueDequeueResult<TItem>> TryDequeueAsync(CancellationToken cancellationToken)
|
public async Task<AsyncQueueDequeueResult<TItem>> TryDequeueAsync(CancellationToken cancellationToken)
|
||||||
@@ -25,12 +24,17 @@ namespace MQTTnet.Internal
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_semaphore == null)
|
||||||
|
{
|
||||||
|
return new AsyncQueueDequeueResult<TItem>(false, default);
|
||||||
|
}
|
||||||
|
|
||||||
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
return new AsyncQueueDequeueResult<TItem>(false, default(TItem));
|
return new AsyncQueueDequeueResult<TItem>(false, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_queue.TryDequeue(out var item))
|
if (_queue.TryDequeue(out var item))
|
||||||
@@ -39,7 +43,7 @@ namespace MQTTnet.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AsyncQueueDequeueResult<TItem>(false, default(TItem));
|
return new AsyncQueueDequeueResult<TItem>(false, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AsyncQueueDequeueResult<TItem> TryDequeue()
|
public AsyncQueueDequeueResult<TItem> TryDequeue()
|
||||||
@@ -49,7 +53,7 @@ namespace MQTTnet.Internal
|
|||||||
return new AsyncQueueDequeueResult<TItem>(true, item);
|
return new AsyncQueueDequeueResult<TItem>(true, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AsyncQueueDequeueResult<TItem>(false, default(TItem));
|
return new AsyncQueueDequeueResult<TItem>(false, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
@@ -60,6 +64,7 @@ namespace MQTTnet.Internal
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_semaphore?.Dispose();
|
_semaphore?.Dispose();
|
||||||
|
_semaphore = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace MQTTnet.LowLevelClient
|
|||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
_adapter.Dispose();
|
_adapter?.Dispose();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||||
<AssemblyName>MQTTnet</AssemblyName>
|
<AssemblyName>MQTTnet</AssemblyName>
|
||||||
@@ -14,6 +14,9 @@
|
|||||||
<PackageId />
|
<PackageId />
|
||||||
<SignAssembly>false</SignAssembly>
|
<SignAssembly>false</SignAssembly>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)'=='uap10.0'">
|
<PropertyGroup Condition="'$(TargetFramework)'=='uap10.0'">
|
||||||
@@ -41,6 +44,10 @@
|
|||||||
<DefineConstants>RELEASE;NETSTANDARD1_3</DefineConstants>
|
<DefineConstants>RELEASE;NETSTANDARD1_3</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.3'">
|
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.3'">
|
||||||
<PackageReference Include="System.Net.Security" Version="4.3.2" />
|
<PackageReference Include="System.Net.Security" Version="4.3.2" />
|
||||||
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
|
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using MQTTnet.LowLevelClient;
|
|||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace MQTTnet
|
namespace MQTTnet
|
||||||
{
|
{
|
||||||
@@ -25,6 +26,13 @@ namespace MQTTnet
|
|||||||
|
|
||||||
public IMqttNetLogger DefaultLogger { get; }
|
public IMqttNetLogger DefaultLogger { get; }
|
||||||
|
|
||||||
|
public IList<Func<IMqttFactory, IMqttServerAdapter>> DefaultServerAdapters { get; } = new List<Func<IMqttFactory, IMqttServerAdapter>>
|
||||||
|
{
|
||||||
|
factory => new MqttTcpServerAdapter(factory.DefaultLogger)
|
||||||
|
};
|
||||||
|
|
||||||
|
public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
|
||||||
|
|
||||||
public IMqttFactory UseClientAdapterFactory(IMqttClientAdapterFactory clientAdapterFactory)
|
public IMqttFactory UseClientAdapterFactory(IMqttClientAdapterFactory clientAdapterFactory)
|
||||||
{
|
{
|
||||||
_clientAdapterFactory = clientAdapterFactory ?? throw new ArgumentNullException(nameof(clientAdapterFactory));
|
_clientAdapterFactory = clientAdapterFactory ?? throw new ArgumentNullException(nameof(clientAdapterFactory));
|
||||||
@@ -50,10 +58,10 @@ namespace MQTTnet
|
|||||||
return new LowLevelMqttClient(_clientAdapterFactory, DefaultLogger);
|
return new LowLevelMqttClient(_clientAdapterFactory, DefaultLogger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory clientAdapterFactoryy)
|
public ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory clientAdapterFactory)
|
||||||
{
|
{
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||||
if (clientAdapterFactoryy == null) throw new ArgumentNullException(nameof(clientAdapterFactoryy));
|
if (clientAdapterFactory == null) throw new ArgumentNullException(nameof(clientAdapterFactory));
|
||||||
|
|
||||||
return new LowLevelMqttClient(_clientAdapterFactory, logger);
|
return new LowLevelMqttClient(_clientAdapterFactory, logger);
|
||||||
}
|
}
|
||||||
@@ -94,7 +102,8 @@ namespace MQTTnet
|
|||||||
{
|
{
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
return CreateMqttServer(new List<IMqttServerAdapter> { new MqttTcpServerAdapter(logger) }, logger);
|
var serverAdapters = DefaultServerAdapters.Select(a => a.Invoke(this));
|
||||||
|
return CreateMqttServer(serverAdapters, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> serverAdapters, IMqttNetLogger logger)
|
public IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> serverAdapters, IMqttNetLogger logger)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System;
|
|||||||
|
|
||||||
namespace MQTTnet
|
namespace MQTTnet
|
||||||
{
|
{
|
||||||
[Obsolete("Use MqttTopicFilter instead. It is just a renamed version to align with general namings in this lib.")]
|
[Obsolete("Use MqttTopicFilterBuilder instead. It is just a renamed version to align with general namings in this lib.")]
|
||||||
public class TopicFilterBuilder : MqttTopicFilterBuilder
|
public class TopicFilterBuilder : MqttTopicFilterBuilder
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using MQTTnet.Server.Status;
|
using System;
|
||||||
|
using MQTTnet.Server.Status;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public interface IMqttServer : IApplicationMessageReceiver, IApplicationMessagePublisher
|
public interface IMqttServer : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
|
||||||
{
|
{
|
||||||
bool IsStarted { get; }
|
bool IsStarted { get; }
|
||||||
IMqttServerStartedHandler StartedHandler { get; set; }
|
IMqttServerStartedHandler StartedHandler { get; set; }
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
public interface IMqttServerCertificateCredentials
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
string Password { get; }
|
public interface IMqttServerCertificateCredentials
|
||||||
|
{
|
||||||
|
string Password { get; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
|
|
||||||
@@ -6,6 +7,8 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
public interface IMqttServerFactory
|
public interface IMqttServerFactory
|
||||||
{
|
{
|
||||||
|
IList<Func<IMqttFactory, IMqttServerAdapter>> DefaultServerAdapters { get; }
|
||||||
|
|
||||||
IMqttServer CreateMqttServer();
|
IMqttServer CreateMqttServer();
|
||||||
|
|
||||||
IMqttServer CreateMqttServer(IMqttNetLogger logger);
|
IMqttServer CreateMqttServer(IMqttNetLogger logger);
|
||||||
|
|||||||
@@ -25,5 +25,9 @@ namespace MQTTnet.Server
|
|||||||
IMqttServerStorage Storage { get; }
|
IMqttServerStorage Storage { get; }
|
||||||
|
|
||||||
IMqttRetainedMessagesManager RetainedMessagesManager { get; }
|
IMqttRetainedMessagesManager RetainedMessagesManager { get; }
|
||||||
|
|
||||||
|
IMqttServerApplicationMessageInterceptor UndeliveredMessageInterceptor { get; set; }
|
||||||
|
|
||||||
|
IMqttServerClientDisconnectedHandler ClientDisconnectedInterceptor { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,8 +23,6 @@ namespace MQTTnet.Server
|
|||||||
readonly CancellationTokenSource _cancellationToken = new CancellationTokenSource();
|
readonly CancellationTokenSource _cancellationToken = new CancellationTokenSource();
|
||||||
|
|
||||||
readonly IMqttRetainedMessagesManager _retainedMessagesManager;
|
readonly IMqttRetainedMessagesManager _retainedMessagesManager;
|
||||||
readonly Func<Task> _onStart;
|
|
||||||
readonly Func<MqttClientDisconnectType, Task> _onStop;
|
|
||||||
readonly MqttClientKeepAliveMonitor _keepAliveMonitor;
|
readonly MqttClientKeepAliveMonitor _keepAliveMonitor;
|
||||||
readonly MqttClientSessionsManager _sessionsManager;
|
readonly MqttClientSessionsManager _sessionsManager;
|
||||||
|
|
||||||
@@ -32,6 +30,7 @@ namespace MQTTnet.Server
|
|||||||
readonly IMqttServerOptions _serverOptions;
|
readonly IMqttServerOptions _serverOptions;
|
||||||
|
|
||||||
readonly IMqttChannelAdapter _channelAdapter;
|
readonly IMqttChannelAdapter _channelAdapter;
|
||||||
|
readonly MqttConnectionValidatorContext _connectionValidatorContext;
|
||||||
readonly IMqttDataConverter _dataConverter;
|
readonly IMqttDataConverter _dataConverter;
|
||||||
readonly string _endpoint;
|
readonly string _endpoint;
|
||||||
readonly DateTime _connectedTimestamp;
|
readonly DateTime _connectedTimestamp;
|
||||||
@@ -47,25 +46,22 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
volatile bool _isTakeover;
|
volatile bool _isTakeover;
|
||||||
|
|
||||||
public MqttClientConnection(
|
public MqttClientConnection(MqttConnectPacket connectPacket,
|
||||||
MqttConnectPacket connectPacket,
|
|
||||||
IMqttChannelAdapter channelAdapter,
|
IMqttChannelAdapter channelAdapter,
|
||||||
MqttClientSession session,
|
MqttClientSession session,
|
||||||
|
MqttConnectionValidatorContext connectionValidatorContext,
|
||||||
IMqttServerOptions serverOptions,
|
IMqttServerOptions serverOptions,
|
||||||
MqttClientSessionsManager sessionsManager,
|
MqttClientSessionsManager sessionsManager,
|
||||||
IMqttRetainedMessagesManager retainedMessagesManager,
|
IMqttRetainedMessagesManager retainedMessagesManager,
|
||||||
Func<Task> onStart,
|
|
||||||
Func<MqttClientDisconnectType, Task> onStop,
|
|
||||||
IMqttNetLogger logger)
|
IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
Session = session ?? throw new ArgumentNullException(nameof(session));
|
Session = session ?? throw new ArgumentNullException(nameof(session));
|
||||||
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
|
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
|
||||||
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
|
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
|
||||||
_retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager));
|
_retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager));
|
||||||
_onStart = onStart ?? throw new ArgumentNullException(nameof(onStart));
|
|
||||||
_onStop = onStop ?? throw new ArgumentNullException(nameof(onStop));
|
|
||||||
|
|
||||||
_channelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter));
|
_channelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter));
|
||||||
|
_connectionValidatorContext = connectionValidatorContext ?? throw new ArgumentNullException(nameof(connectionValidatorContext));
|
||||||
_dataConverter = _channelAdapter.PacketFormatterAdapter.DataConverter;
|
_dataConverter = _channelAdapter.PacketFormatterAdapter.DataConverter;
|
||||||
_endpoint = _channelAdapter.Endpoint;
|
_endpoint = _channelAdapter.Endpoint;
|
||||||
ConnectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
|
ConnectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
|
||||||
@@ -131,18 +127,17 @@ namespace MQTTnet.Server
|
|||||||
_cancellationToken.Dispose();
|
_cancellationToken.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task RunAsync(MqttConnectionValidatorContext connectionValidatorContext)
|
public Task RunAsync()
|
||||||
{
|
{
|
||||||
_packageReceiverTask = RunInternalAsync(connectionValidatorContext);
|
_packageReceiverTask = RunInternalAsync();
|
||||||
return _packageReceiverTask;
|
return _packageReceiverTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task RunInternalAsync(MqttConnectionValidatorContext connectionValidatorContext)
|
async Task RunInternalAsync()
|
||||||
{
|
{
|
||||||
var disconnectType = MqttClientDisconnectType.NotClean;
|
var disconnectType = MqttClientDisconnectType.NotClean;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _onStart();
|
|
||||||
_logger.Info("Client '{0}': Session started.", ClientId);
|
_logger.Info("Client '{0}': Session started.", ClientId);
|
||||||
|
|
||||||
_channelAdapter.ReadingPacketStartedCallback = OnAdapterReadingPacketStarted;
|
_channelAdapter.ReadingPacketStartedCallback = OnAdapterReadingPacketStarted;
|
||||||
@@ -155,9 +150,7 @@ namespace MQTTnet.Server
|
|||||||
// TODO: Change to single thread in SessionManager. Or use SessionManager and stats from KeepAliveMonitor.
|
// TODO: Change to single thread in SessionManager. Or use SessionManager and stats from KeepAliveMonitor.
|
||||||
_keepAliveMonitor.Start(ConnectPacket.KeepAlivePeriod, _cancellationToken.Token);
|
_keepAliveMonitor.Start(ConnectPacket.KeepAlivePeriod, _cancellationToken.Token);
|
||||||
|
|
||||||
await SendAsync(
|
await SendAsync(_channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(_connectionValidatorContext)).ConfigureAwait(false);
|
||||||
_channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(connectionValidatorContext)
|
|
||||||
).ConfigureAwait(false);
|
|
||||||
|
|
||||||
Session.IsCleanSession = false;
|
Session.IsCleanSession = false;
|
||||||
|
|
||||||
@@ -262,17 +255,33 @@ namespace MQTTnet.Server
|
|||||||
_channelAdapter.ReadingPacketStartedCallback = null;
|
_channelAdapter.ReadingPacketStartedCallback = null;
|
||||||
_channelAdapter.ReadingPacketCompletedCallback = null;
|
_channelAdapter.ReadingPacketCompletedCallback = null;
|
||||||
|
|
||||||
_logger.Info("Client '{0}': Connection stopped.", ClientId);
|
|
||||||
|
|
||||||
_packageReceiverTask = null;
|
_packageReceiverTask = null;
|
||||||
|
|
||||||
|
if (_isTakeover)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Don't use SendAsync here _cancellationToken is already cancelled.
|
||||||
|
await _channelAdapter.SendPacketAsync(new MqttDisconnectPacket
|
||||||
|
{
|
||||||
|
ReasonCode = MqttDisconnectReasonCode.SessionTakenOver
|
||||||
|
}, TimeSpan.Zero, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.Error(exception, "Client '{0}': Error while sending DISCONNECT packet after takeover.", ClientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Info("Client '{0}': Connection stopped.", ClientId);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _onStop(disconnectType);
|
await _sessionsManager.CleanUpClient(ClientId, _channelAdapter, disconnectType);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.Error(e, "client '{0}': Error while cleaning up", ClientId);
|
_logger.Error(e, "Client '{0}': Error while cleaning up", ClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,22 +401,18 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
publishPacket = _dataConverter.CreatePublishPacket(queuedApplicationMessage.ApplicationMessage);
|
publishPacket = _dataConverter.CreatePublishPacket(queuedApplicationMessage.ApplicationMessage);
|
||||||
publishPacket.QualityOfServiceLevel = queuedApplicationMessage.QualityOfServiceLevel;
|
publishPacket.QualityOfServiceLevel = queuedApplicationMessage.SubscriptionQualityOfServiceLevel;
|
||||||
|
|
||||||
// Set the retain flag to true according to [MQTT-3.3.1-8] and [MQTT-3.3.1-9].
|
// Set the retain flag to true according to [MQTT-3.3.1-8] and [MQTT-3.3.1-9].
|
||||||
publishPacket.Retain = queuedApplicationMessage.IsRetainedMessage;
|
publishPacket.Retain = queuedApplicationMessage.IsRetainedMessage;
|
||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel > 0)
|
|
||||||
{
|
|
||||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_serverOptions.ClientMessageQueueInterceptor != null)
|
if (_serverOptions.ClientMessageQueueInterceptor != null)
|
||||||
{
|
{
|
||||||
var context = new MqttClientMessageQueueInterceptorContext(
|
var context = new MqttClientMessageQueueInterceptorContext(
|
||||||
queuedApplicationMessage.SenderClientId,
|
queuedApplicationMessage.SenderClientId,
|
||||||
ClientId,
|
ClientId,
|
||||||
queuedApplicationMessage.ApplicationMessage);
|
queuedApplicationMessage.ApplicationMessage,
|
||||||
|
queuedApplicationMessage.SubscriptionQualityOfServiceLevel);
|
||||||
|
|
||||||
if (_serverOptions.ClientMessageQueueInterceptor != null)
|
if (_serverOptions.ClientMessageQueueInterceptor != null)
|
||||||
{
|
{
|
||||||
@@ -421,7 +426,12 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
publishPacket.Topic = context.ApplicationMessage.Topic;
|
publishPacket.Topic = context.ApplicationMessage.Topic;
|
||||||
publishPacket.Payload = context.ApplicationMessage.Payload;
|
publishPacket.Payload = context.ApplicationMessage.Payload;
|
||||||
publishPacket.QualityOfServiceLevel = context.ApplicationMessage.QualityOfServiceLevel;
|
publishPacket.QualityOfServiceLevel = context.SubscriptionQualityOfServiceLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publishPacket.QualityOfServiceLevel > 0)
|
||||||
|
{
|
||||||
|
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
namespace MQTTnet.Server
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public class MqttClientMessageQueueInterceptorContext
|
public class MqttClientMessageQueueInterceptorContext
|
||||||
{
|
{
|
||||||
public MqttClientMessageQueueInterceptorContext(string senderClientId, string receiverClientId, MqttApplicationMessage applicationMessage)
|
public MqttClientMessageQueueInterceptorContext(string senderClientId, string receiverClientId, MqttApplicationMessage applicationMessage, MqttQualityOfServiceLevel subscriptionQualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
SenderClientId = senderClientId;
|
SenderClientId = senderClientId;
|
||||||
ReceiverClientId = receiverClientId;
|
ReceiverClientId = receiverClientId;
|
||||||
ApplicationMessage = applicationMessage;
|
ApplicationMessage = applicationMessage;
|
||||||
|
SubscriptionQualityOfServiceLevel = subscriptionQualityOfServiceLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SenderClientId { get; }
|
public string SenderClientId { get; }
|
||||||
@@ -16,5 +19,7 @@
|
|||||||
public MqttApplicationMessage ApplicationMessage { get; set; }
|
public MqttApplicationMessage ApplicationMessage { get; set; }
|
||||||
|
|
||||||
public bool AcceptEnqueue { get; set; } = true;
|
public bool AcceptEnqueue { get; set; } = true;
|
||||||
|
|
||||||
|
public MqttQualityOfServiceLevel SubscriptionQualityOfServiceLevel { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server
|
||||||
|
{
|
||||||
|
public class MqttClientMessageQueueInterceptorDelegate : IMqttServerClientMessageQueueInterceptor
|
||||||
|
{
|
||||||
|
private readonly Func<MqttClientMessageQueueInterceptorContext, Task> _callback;
|
||||||
|
|
||||||
|
public MqttClientMessageQueueInterceptorDelegate(Action<MqttClientMessageQueueInterceptorContext> callback)
|
||||||
|
{
|
||||||
|
if (callback == null) throw new ArgumentNullException(nameof(callback));
|
||||||
|
|
||||||
|
_callback = context =>
|
||||||
|
{
|
||||||
|
callback(context);
|
||||||
|
return Task.FromResult(0);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttClientMessageQueueInterceptorDelegate(Func<MqttClientMessageQueueInterceptorContext, Task> callback)
|
||||||
|
{
|
||||||
|
_callback = callback ?? throw new ArgumentNullException(nameof(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InterceptClientMessageQueueEnqueueAsync(MqttClientMessageQueueInterceptorContext context)
|
||||||
|
{
|
||||||
|
return _callback(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,17 +46,19 @@ namespace MQTTnet.Server
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IDictionary<object, object> Items { get; }
|
public IDictionary<object, object> Items { get; }
|
||||||
|
|
||||||
public void EnqueueApplicationMessage(MqttApplicationMessage applicationMessage, string senderClientId, bool isRetainedApplicationMessage)
|
public bool EnqueueApplicationMessage(MqttApplicationMessage applicationMessage, string senderClientId, bool isRetainedApplicationMessage)
|
||||||
{
|
{
|
||||||
var checkSubscriptionsResult = SubscriptionsManager.CheckSubscriptions(applicationMessage.Topic, applicationMessage.QualityOfServiceLevel);
|
var checkSubscriptionsResult = SubscriptionsManager.CheckSubscriptions(applicationMessage.Topic, applicationMessage.QualityOfServiceLevel);
|
||||||
if (!checkSubscriptionsResult.IsSubscribed)
|
if (!checkSubscriptionsResult.IsSubscribed)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Verbose("Queued application message with topic '{0}' (ClientId: {1}).", applicationMessage.Topic, ClientId);
|
_logger.Verbose("Queued application message with topic '{0}' (ClientId: {1}).", applicationMessage.Topic, ClientId);
|
||||||
|
|
||||||
ApplicationMessagesQueue.Enqueue(applicationMessage, senderClientId, checkSubscriptionsResult.QualityOfServiceLevel, isRetainedApplicationMessage);
|
ApplicationMessagesQueue.Enqueue(applicationMessage, senderClientId, checkSubscriptionsResult.QualityOfServiceLevel, isRetainedApplicationMessage);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SubscribeAsync(ICollection<MqttTopicFilter> topicFilters)
|
public async Task SubscribeAsync(ICollection<MqttTopicFilter> topicFilters)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
ApplicationMessage = applicationMessage,
|
ApplicationMessage = applicationMessage,
|
||||||
SenderClientId = senderClientId,
|
SenderClientId = senderClientId,
|
||||||
QualityOfServiceLevel = qualityOfServiceLevel,
|
SubscriptionQualityOfServiceLevel = qualityOfServiceLevel,
|
||||||
IsRetainedMessage = isRetainedMessage
|
IsRetainedMessage = isRetainedMessage
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,14 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public class MqttClientSessionsManager : Disposable
|
public sealed class MqttClientSessionsManager : IDisposable
|
||||||
{
|
{
|
||||||
readonly AsyncQueue<MqttEnqueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttEnqueuedApplicationMessage>();
|
readonly AsyncQueue<MqttEnqueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttEnqueuedApplicationMessage>();
|
||||||
|
|
||||||
readonly AsyncLock _createConnectionGate = new AsyncLock();
|
readonly AsyncLock _createConnectionGate = new AsyncLock();
|
||||||
readonly ConcurrentDictionary<string, MqttClientConnection> _connections = new ConcurrentDictionary<string, MqttClientConnection>();
|
readonly ConcurrentDictionary<string, MqttClientConnection> _connections = new ConcurrentDictionary<string, MqttClientConnection>();
|
||||||
readonly ConcurrentDictionary<string, MqttClientSession> _sessions = new ConcurrentDictionary<string, MqttClientSession>();
|
readonly ConcurrentDictionary<string, MqttClientSession> _sessions = new ConcurrentDictionary<string, MqttClientSession>();
|
||||||
|
|
||||||
readonly IDictionary<object, object> _serverSessionItems = new ConcurrentDictionary<object, object>();
|
readonly IDictionary<object, object> _serverSessionItems = new ConcurrentDictionary<object, object>();
|
||||||
|
|
||||||
readonly CancellationToken _cancellationToken;
|
readonly CancellationToken _cancellationToken;
|
||||||
@@ -150,13 +151,9 @@ namespace MQTTnet.Server
|
|||||||
_logger.Verbose("Session for client '{0}' deleted.", clientId);
|
_logger.Verbose("Session for client '{0}' deleted.", clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (disposing)
|
_messageQueue?.Dispose();
|
||||||
{
|
|
||||||
_messageQueue?.Dispose();
|
|
||||||
}
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task TryProcessQueuedApplicationMessagesAsync(CancellationToken cancellationToken)
|
async Task TryProcessQueuedApplicationMessagesAsync(CancellationToken cancellationToken)
|
||||||
@@ -187,7 +184,6 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dequeueResult = await _messageQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);
|
var dequeueResult = await _messageQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!dequeueResult.IsSuccess)
|
if (!dequeueResult.IsSuccess)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -224,12 +220,31 @@ namespace MQTTnet.Server
|
|||||||
await _retainedMessagesManager.HandleMessageAsync(sender?.ClientId, applicationMessage).ConfigureAwait(false);
|
await _retainedMessagesManager.HandleMessageAsync(sender?.ClientId, applicationMessage).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deliveryCount = 0;
|
||||||
|
|
||||||
foreach (var clientSession in _sessions.Values)
|
foreach (var clientSession in _sessions.Values)
|
||||||
{
|
{
|
||||||
clientSession.EnqueueApplicationMessage(
|
var isSubscribed = clientSession.EnqueueApplicationMessage(
|
||||||
applicationMessage,
|
applicationMessage,
|
||||||
sender?.ClientId,
|
sender?.ClientId,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
|
if (isSubscribed)
|
||||||
|
{
|
||||||
|
deliveryCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deliveryCount == 0)
|
||||||
|
{
|
||||||
|
var undeliveredMessageInterceptor = _options.UndeliveredMessageInterceptor;
|
||||||
|
|
||||||
|
if (undeliveredMessageInterceptor == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await undeliveredMessageInterceptor.InterceptApplicationMessagePublishAsync(new MqttApplicationMessageInterceptorContext(sender?.ClientId, sender?.Session?.Items, applicationMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
@@ -243,11 +258,9 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
string clientId = null;
|
|
||||||
|
|
||||||
MqttConnectPacket connectPacket;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
MqttConnectPacket connectPacket;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var firstPacket = await channelAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false);
|
var firstPacket = await channelAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false);
|
||||||
@@ -276,17 +289,9 @@ namespace MQTTnet.Server
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
clientId = connectPacket.ClientId;
|
var connection = await CreateClientConnectionAsync(connectPacket, connectionValidatorContext, channelAdapter).ConfigureAwait(false);
|
||||||
|
await _eventDispatcher.SafeNotifyClientConnectedAsync(connectPacket.ClientId).ConfigureAwait(false);
|
||||||
var connection = await CreateClientConnectionAsync(
|
await connection.RunAsync().ConfigureAwait(false);
|
||||||
connectPacket,
|
|
||||||
connectionValidatorContext,
|
|
||||||
channelAdapter,
|
|
||||||
async () => await _eventDispatcher.SafeNotifyClientConnectedAsync(clientId).ConfigureAwait(false),
|
|
||||||
async disconnectType => await CleanUpClient(clientId, channelAdapter, disconnectType)
|
|
||||||
).ConfigureAwait(false);
|
|
||||||
|
|
||||||
await connection.RunAsync(connectionValidatorContext).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -297,15 +302,19 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CleanUpClient(string clientId, IMqttChannelAdapter channelAdapter, MqttClientDisconnectType disconnectType)
|
public async Task CleanUpClient(string clientId, IMqttChannelAdapter channelAdapter, MqttClientDisconnectType disconnectType)
|
||||||
{
|
{
|
||||||
if (clientId != null)
|
if (clientId != null)
|
||||||
{
|
{
|
||||||
_connections.TryRemove(clientId, out _);
|
// in case it is a takeover _connections already contains the new connection
|
||||||
|
if (disconnectType != MqttClientDisconnectType.Takeover)
|
||||||
if (!_options.EnablePersistentSessions)
|
|
||||||
{
|
{
|
||||||
await DeleteSessionAsync(clientId).ConfigureAwait(false);
|
_connections.TryRemove(clientId, out _);
|
||||||
|
|
||||||
|
if (!_options.EnablePersistentSessions)
|
||||||
|
{
|
||||||
|
await DeleteSessionAsync(clientId).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,45 +354,52 @@ namespace MQTTnet.Server
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttClientConnection> CreateClientConnectionAsync(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter, Func<Task> onStart, Func<MqttClientDisconnectType, Task> onStop)
|
async Task<MqttClientConnection> CreateClientConnectionAsync(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter)
|
||||||
{
|
{
|
||||||
|
MqttClientConnection connection;
|
||||||
|
MqttClientConnection existingConnection = null;
|
||||||
|
|
||||||
using (await _createConnectionGate.WaitAsync(_cancellationToken).ConfigureAwait(false))
|
using (await _createConnectionGate.WaitAsync(_cancellationToken).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
var isSessionPresent = _sessions.TryGetValue(connectPacket.ClientId, out var session);
|
var session = _sessions.AddOrUpdate(
|
||||||
|
connectPacket.ClientId,
|
||||||
var isConnectionPresent = _connections.TryGetValue(connectPacket.ClientId, out var existingConnection);
|
key =>
|
||||||
if (isConnectionPresent)
|
|
||||||
{
|
|
||||||
await existingConnection.StopAsync(true).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSessionPresent)
|
|
||||||
{
|
|
||||||
if (connectPacket.CleanSession)
|
|
||||||
{
|
{
|
||||||
session = null;
|
_logger.Verbose("Created a new session for client '{0}'.", key);
|
||||||
|
return new MqttClientSession(key, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _retainedMessagesManager, _rootLogger);
|
||||||
_logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId);
|
},
|
||||||
}
|
(key, existingSession) =>
|
||||||
else
|
|
||||||
{
|
{
|
||||||
|
if (connectPacket.CleanSession)
|
||||||
|
{
|
||||||
|
_logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId);
|
||||||
|
return new MqttClientSession(key, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _retainedMessagesManager, _rootLogger);
|
||||||
|
}
|
||||||
|
|
||||||
_logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId);
|
_logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId);
|
||||||
}
|
return existingSession;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (session == null)
|
connection = new MqttClientConnection(connectPacket, channelAdapter, session, connectionValidatorContext, _options, this, _retainedMessagesManager, _rootLogger);
|
||||||
{
|
|
||||||
session = new MqttClientSession(connectPacket.ClientId, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _retainedMessagesManager, _rootLogger);
|
|
||||||
_logger.Verbose("Created a new session for client '{0}'.", connectPacket.ClientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
var connection = new MqttClientConnection(connectPacket, channelAdapter, session, _options, this, _retainedMessagesManager, onStart, onStop, _rootLogger);
|
_connections.AddOrUpdate(
|
||||||
|
connectPacket.ClientId,
|
||||||
_connections[connection.ClientId] = connection;
|
key => connection,
|
||||||
_sessions[session.ClientId] = session;
|
(key, tempExistingConnection) =>
|
||||||
|
{
|
||||||
return connection;
|
existingConnection = tempExistingConnection;
|
||||||
|
return connection;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disconnect the client outside of the lock so that new clients can still connect while a single
|
||||||
|
// one is being disconnected.
|
||||||
|
if (existingConnection != null)
|
||||||
|
{
|
||||||
|
await existingConnection.StopAsync(true).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttApplicationMessageInterceptorContext> InterceptApplicationMessageAsync(MqttClientConnection senderConnection, MqttApplicationMessage applicationMessage)
|
async Task<MqttApplicationMessageInterceptorContext> InterceptApplicationMessageAsync(MqttClientConnection senderConnection, MqttApplicationMessage applicationMessage)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MQTTnet.Protocol;
|
using System;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
@@ -10,7 +11,14 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public bool IsRetainedMessage { get; set; }
|
public bool IsRetainedMessage { get; set; }
|
||||||
|
|
||||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
public MqttQualityOfServiceLevel SubscriptionQualityOfServiceLevel { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Use 'SubscriptionQualityOfServiceLevel' instead.")]
|
||||||
|
public MqttQualityOfServiceLevel QualityOfServiceLevel
|
||||||
|
{
|
||||||
|
get => SubscriptionQualityOfServiceLevel;
|
||||||
|
set => SubscriptionQualityOfServiceLevel = value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsDuplicate { get; set; }
|
public bool IsDuplicate { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public class MqttServer : IMqttServer
|
public class MqttServer : Disposable, IMqttServer
|
||||||
{
|
{
|
||||||
readonly MqttServerEventDispatcher _eventDispatcher;
|
readonly MqttServerEventDispatcher _eventDispatcher;
|
||||||
readonly ICollection<IMqttServerAdapter> _adapters;
|
readonly ICollection<IMqttServerAdapter> _adapters;
|
||||||
@@ -76,23 +78,43 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public Task<IList<IMqttClientStatus>> GetClientStatusAsync()
|
public Task<IList<IMqttClientStatus>> GetClientStatusAsync()
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
ThrowIfNotStarted();
|
||||||
|
|
||||||
return _clientSessionsManager.GetClientStatusAsync();
|
return _clientSessionsManager.GetClientStatusAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IList<IMqttSessionStatus>> GetSessionStatusAsync()
|
public Task<IList<IMqttSessionStatus>> GetSessionStatusAsync()
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
ThrowIfNotStarted();
|
||||||
|
|
||||||
return _clientSessionsManager.GetSessionStatusAsync();
|
return _clientSessionsManager.GetSessionStatusAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IList<MqttApplicationMessage>> GetRetainedApplicationMessagesAsync()
|
public Task<IList<MqttApplicationMessage>> GetRetainedApplicationMessagesAsync()
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
ThrowIfNotStarted();
|
||||||
|
|
||||||
return _retainedMessagesManager.GetMessagesAsync();
|
return _retainedMessagesManager.GetMessagesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task ClearRetainedApplicationMessagesAsync()
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
ThrowIfNotStarted();
|
||||||
|
|
||||||
|
return _retainedMessagesManager?.ClearMessagesAsync() ?? PlatformAbstractionLayer.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
public Task SubscribeAsync(string clientId, ICollection<MqttTopicFilter> topicFilters)
|
public Task SubscribeAsync(string clientId, ICollection<MqttTopicFilter> topicFilters)
|
||||||
{
|
{
|
||||||
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
|
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
|
||||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||||
|
|
||||||
|
ThrowIfDisposed();
|
||||||
|
ThrowIfNotStarted();
|
||||||
|
|
||||||
return _clientSessionsManager.SubscribeAsync(clientId, topicFilters);
|
return _clientSessionsManager.SubscribeAsync(clientId, topicFilters);
|
||||||
}
|
}
|
||||||
@@ -102,6 +124,9 @@ namespace MQTTnet.Server
|
|||||||
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
|
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
|
||||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||||
|
|
||||||
|
ThrowIfDisposed();
|
||||||
|
ThrowIfNotStarted();
|
||||||
|
|
||||||
return _clientSessionsManager.UnsubscribeAsync(clientId, topicFilters);
|
return _clientSessionsManager.UnsubscribeAsync(clientId, topicFilters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,9 +134,11 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
|
||||||
|
ThrowIfDisposed();
|
||||||
|
|
||||||
MqttTopicValidator.ThrowIfInvalid(applicationMessage.Topic);
|
MqttTopicValidator.ThrowIfInvalid(applicationMessage.Topic);
|
||||||
|
|
||||||
if (_cancellationTokenSource == null) throw new InvalidOperationException("The server is not started.");
|
ThrowIfNotStarted();
|
||||||
|
|
||||||
_clientSessionsManager.DispatchApplicationMessage(applicationMessage, null);
|
_clientSessionsManager.DispatchApplicationMessage(applicationMessage, null);
|
||||||
|
|
||||||
@@ -120,16 +147,16 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public async Task StartAsync(IMqttServerOptions options)
|
public async Task StartAsync(IMqttServerOptions options)
|
||||||
{
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
ThrowIfStarted();
|
||||||
|
|
||||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
if (Options.RetainedMessagesManager == null) throw new MqttConfigurationException("options.RetainedMessagesManager should not be null.");
|
|
||||||
|
|
||||||
if (_cancellationTokenSource != null) throw new InvalidOperationException("The server is already started.");
|
|
||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
_retainedMessagesManager = Options.RetainedMessagesManager;
|
_retainedMessagesManager = Options.RetainedMessagesManager ?? throw new MqttConfigurationException("options.RetainedMessagesManager should not be null.");
|
||||||
await _retainedMessagesManager.Start(Options, _rootLogger);
|
|
||||||
|
await _retainedMessagesManager.Start(Options, _rootLogger).ConfigureAwait(false);
|
||||||
await _retainedMessagesManager.LoadMessagesAsync().ConfigureAwait(false);
|
await _retainedMessagesManager.LoadMessagesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
_clientSessionsManager = new MqttClientSessionsManager(Options, _retainedMessagesManager, _cancellationTokenSource.Token, _eventDispatcher, _rootLogger);
|
_clientSessionsManager = new MqttClientSessionsManager(Options, _retainedMessagesManager, _cancellationTokenSource.Token, _eventDispatcher, _rootLogger);
|
||||||
@@ -173,13 +200,13 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
_clientSessionsManager?.Dispose();
|
||||||
|
_clientSessionsManager = null;
|
||||||
|
|
||||||
_cancellationTokenSource?.Dispose();
|
_cancellationTokenSource?.Dispose();
|
||||||
_cancellationTokenSource = null;
|
_cancellationTokenSource = null;
|
||||||
|
|
||||||
_retainedMessagesManager = null;
|
_retainedMessagesManager = null;
|
||||||
|
|
||||||
_clientSessionsManager?.Dispose();
|
|
||||||
_clientSessionsManager = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var stoppedHandler = StoppedHandler;
|
var stoppedHandler = StoppedHandler;
|
||||||
@@ -189,14 +216,40 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task ClearRetainedApplicationMessagesAsync()
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
return _retainedMessagesManager?.ClearMessagesAsync();
|
if (disposing)
|
||||||
|
{
|
||||||
|
StopAsync().GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
foreach (var adapter in _adapters)
|
||||||
|
{
|
||||||
|
adapter.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
Task OnHandleClient(IMqttChannelAdapter channelAdapter)
|
Task OnHandleClient(IMqttChannelAdapter channelAdapter)
|
||||||
{
|
{
|
||||||
return _clientSessionsManager.HandleClientConnectionAsync(channelAdapter);
|
return _clientSessionsManager.HandleClientConnectionAsync(channelAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ThrowIfStarted()
|
||||||
|
{
|
||||||
|
if (_cancellationTokenSource != null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The MQTT server is already started.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThrowIfNotStarted()
|
||||||
|
{
|
||||||
|
if (_cancellationTokenSource == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The MQTT server is not started.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,5 +31,9 @@ namespace MQTTnet.Server
|
|||||||
public IMqttServerStorage Storage { get; set; }
|
public IMqttServerStorage Storage { get; set; }
|
||||||
|
|
||||||
public IMqttRetainedMessagesManager RetainedMessagesManager { get; set; } = new MqttRetainedMessagesManager();
|
public IMqttRetainedMessagesManager RetainedMessagesManager { get; set; } = new MqttRetainedMessagesManager();
|
||||||
|
|
||||||
|
public IMqttServerApplicationMessageInterceptor UndeliveredMessageInterceptor { get; set; }
|
||||||
|
|
||||||
|
public IMqttServerClientDisconnectedHandler ClientDisconnectedInterceptor { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,18 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Security;
|
using System.Net.Security;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
|
using MQTTnet.Certificates;
|
||||||
|
|
||||||
|
#if !WINDOWS_UWP
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public class MqttServerOptionsBuilder
|
public class MqttServerOptionsBuilder
|
||||||
{
|
{
|
||||||
private readonly MqttServerOptions _options = new MqttServerOptions();
|
readonly MqttServerOptions _options = new MqttServerOptions();
|
||||||
|
|
||||||
public MqttServerOptionsBuilder WithConnectionBacklog(int value)
|
public MqttServerOptionsBuilder WithConnectionBacklog(int value)
|
||||||
{
|
{
|
||||||
_options.DefaultEndpointOptions.ConnectionBacklog = value;
|
_options.DefaultEndpointOptions.ConnectionBacklog = value;
|
||||||
@@ -82,13 +87,28 @@ namespace MQTTnet.Server
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !WINDOWS_UWP
|
||||||
public MqttServerOptionsBuilder WithEncryptionCertificate(byte[] value, IMqttServerCertificateCredentials credentials = null)
|
public MqttServerOptionsBuilder WithEncryptionCertificate(byte[] value, IMqttServerCertificateCredentials credentials = null)
|
||||||
{
|
{
|
||||||
_options.TlsEndpointOptions.Certificate = value;
|
if (value == null) throw new ArgumentNullException(nameof(value));
|
||||||
_options.TlsEndpointOptions.CertificateCredentials = credentials;
|
|
||||||
|
_options.TlsEndpointOptions.CertificateProvider = new BlobCertificateProvider(value)
|
||||||
|
{
|
||||||
|
Password = credentials?.Password
|
||||||
|
};
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttServerOptionsBuilder WithEncryptionCertificate(X509Certificate2 certificate)
|
||||||
|
{
|
||||||
|
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
|
||||||
|
|
||||||
|
_options.TlsEndpointOptions.CertificateProvider = new X509CertificateProvider(certificate);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public MqttServerOptionsBuilder WithEncryptionSslProtocol(SslProtocols value)
|
public MqttServerOptionsBuilder WithEncryptionSslProtocol(SslProtocols value)
|
||||||
{
|
{
|
||||||
_options.TlsEndpointOptions.SslProtocol = value;
|
_options.TlsEndpointOptions.SslProtocol = value;
|
||||||
@@ -143,6 +163,18 @@ namespace MQTTnet.Server
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttServerOptionsBuilder WithDisconnectedInterceptor(IMqttServerClientDisconnectedHandler value)
|
||||||
|
{
|
||||||
|
_options.ClientDisconnectedInterceptor = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttServerOptionsBuilder WithDisconnectedInterceptor(Action<MqttServerClientDisconnectedEventArgs> value)
|
||||||
|
{
|
||||||
|
_options.ClientDisconnectedInterceptor = new MqttServerClientDisconnectedHandlerDelegate(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttServerOptionsBuilder WithApplicationMessageInterceptor(IMqttServerApplicationMessageInterceptor value)
|
public MqttServerOptionsBuilder WithApplicationMessageInterceptor(IMqttServerApplicationMessageInterceptor value)
|
||||||
{
|
{
|
||||||
_options.ApplicationMessageInterceptor = value;
|
_options.ApplicationMessageInterceptor = value;
|
||||||
@@ -155,6 +187,18 @@ namespace MQTTnet.Server
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttServerOptionsBuilder WithClientMessageQueueInterceptor(IMqttServerClientMessageQueueInterceptor value)
|
||||||
|
{
|
||||||
|
_options.ClientMessageQueueInterceptor = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttServerOptionsBuilder WithClientMessageQueueInterceptor(Action<MqttClientMessageQueueInterceptorContext> value)
|
||||||
|
{
|
||||||
|
_options.ClientMessageQueueInterceptor = new MqttClientMessageQueueInterceptorDelegate(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttServerOptionsBuilder WithSubscriptionInterceptor(IMqttServerSubscriptionInterceptor value)
|
public MqttServerOptionsBuilder WithSubscriptionInterceptor(IMqttServerSubscriptionInterceptor value)
|
||||||
{
|
{
|
||||||
_options.SubscriptionInterceptor = value;
|
_options.SubscriptionInterceptor = value;
|
||||||
@@ -173,6 +217,18 @@ namespace MQTTnet.Server
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttServerOptionsBuilder WithDefaultEndpointReuseAddress()
|
||||||
|
{
|
||||||
|
_options.DefaultEndpointOptions.ReuseAddress = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttServerOptionsBuilder WithTlsEndpointReuseAddress()
|
||||||
|
{
|
||||||
|
_options.TlsEndpointOptions.ReuseAddress = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttServerOptionsBuilder WithPersistentSessions()
|
public MqttServerOptionsBuilder WithPersistentSessions()
|
||||||
{
|
{
|
||||||
_options.EnablePersistentSessions = true;
|
_options.EnablePersistentSessions = true;
|
||||||
@@ -192,5 +248,11 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
return _options;
|
return _options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttServerOptionsBuilder WithUndeliveredMessageInterceptor(Action<MqttApplicationMessageInterceptorContext> value)
|
||||||
|
{
|
||||||
|
_options.UndeliveredMessageInterceptor = new MqttServerApplicationMessageInterceptorDelegate(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,56 @@
|
|||||||
using System.Net.Security;
|
using System;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
|
using MQTTnet.Certificates;
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public class MqttServerTlsTcpEndpointOptions : MqttServerTcpEndpointBaseOptions
|
public class MqttServerTlsTcpEndpointOptions : MqttServerTcpEndpointBaseOptions
|
||||||
{
|
{
|
||||||
|
ICertificateProvider _certificateProvider;
|
||||||
|
|
||||||
public MqttServerTlsTcpEndpointOptions()
|
public MqttServerTlsTcpEndpointOptions()
|
||||||
{
|
{
|
||||||
Port = 8883;
|
Port = 8883;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Please use CertificateProvider with 'BlobCertificateProvider' instead.")]
|
||||||
public byte[] Certificate { get; set; }
|
public byte[] Certificate { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("Please use CertificateProvider with 'BlobCertificateProvider' including password property instead.")]
|
||||||
public IMqttServerCertificateCredentials CertificateCredentials { get; set; }
|
public IMqttServerCertificateCredentials CertificateCredentials { get; set; }
|
||||||
|
|
||||||
|
#if !WINDOWS_UWP
|
||||||
|
public System.Net.Security.RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
|
||||||
|
#endif
|
||||||
|
public ICertificateProvider CertificateProvider
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Backward compatibility only. Gets converted to auto property when
|
||||||
|
// obsolete properties are removed.
|
||||||
|
if (_certificateProvider != null)
|
||||||
|
{
|
||||||
|
return _certificateProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Certificate == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BlobCertificateProvider(Certificate)
|
||||||
|
{
|
||||||
|
Password = CertificateCredentials?.Password
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
set => _certificateProvider = value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool ClientCertificateRequired { get; set; }
|
public bool ClientCertificateRequired { get; set; }
|
||||||
|
|
||||||
public bool CheckCertificateRevocation { get; set; }
|
public bool CheckCertificateRevocation { get; set; }
|
||||||
|
|
||||||
#if !WINDOWS_UWP
|
|
||||||
public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
|
|
||||||
#endif
|
|
||||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,15 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.0" />
|
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
|
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' != 'netcoreapp3.1' ">
|
<ItemGroup Condition=" '$(TargetFramework)' != 'netcoreapp3.1' ">
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.1" />
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ using MQTTnet.Exceptions;
|
|||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using MQTTnet.AspNetCore.Extensions;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests
|
namespace MQTTnet.AspNetCore.Tests
|
||||||
{
|
{
|
||||||
@@ -34,28 +36,46 @@ namespace MQTTnet.AspNetCore.Tests
|
|||||||
|
|
||||||
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(TimeSpan.Zero, CancellationToken.None));
|
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(TimeSpan.Zero, CancellationToken.None));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task TestParallelWrites()
|
public async Task TestCorruptedConnectPacket()
|
||||||
{
|
{
|
||||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311);
|
var writer = new MqttPacketWriter();
|
||||||
|
var serializer = new MqttPacketFormatterAdapter(writer);
|
||||||
var pipe = new DuplexPipeMockup();
|
var pipe = new DuplexPipeMockup();
|
||||||
var connection = new DefaultConnectionContext();
|
var connection = new DefaultConnectionContext();
|
||||||
connection.Transport = pipe;
|
connection.Transport = pipe;
|
||||||
var ctx = new MqttConnectionContext(serializer, connection);
|
var ctx = new MqttConnectionContext(serializer, connection);
|
||||||
|
|
||||||
|
await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, new byte[0]));
|
||||||
|
|
||||||
var tasks = Enumerable.Range(1, 10).Select(_ => Task.Run(async () =>
|
await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(TimeSpan.Zero, CancellationToken.None));
|
||||||
{
|
|
||||||
for (int i = 0; i < 100; i++)
|
|
||||||
{
|
|
||||||
await ctx.SendPacketAsync(new MqttPublishPacket(), TimeSpan.Zero, CancellationToken.None).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
// the first exception should complete the pipes so if someone tries to use the connection after that it should throw immidiatly
|
||||||
|
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => ctx.ReceivePacketAsync(TimeSpan.Zero, CancellationToken.None));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// COMMENTED OUT DUE TO DEAD LOCK? OR VERY VERY SLOW PERFORMANCE ON LOCAL DEV MACHINE. TEST WAS STILL RUNNING AFTER SEVERAL MINUTES!
|
||||||
|
//[TestMethod]
|
||||||
|
//public async Task TestParallelWrites()
|
||||||
|
//{
|
||||||
|
// var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311);
|
||||||
|
// var pipe = new DuplexPipeMockup();
|
||||||
|
// var connection = new DefaultConnectionContext();
|
||||||
|
// connection.Transport = pipe;
|
||||||
|
// var ctx = new MqttConnectionContext(serializer, connection);
|
||||||
|
|
||||||
|
// var tasks = Enumerable.Range(1, 100).Select(_ => Task.Run(async () =>
|
||||||
|
// {
|
||||||
|
// for (int i = 0; i < 100; i++)
|
||||||
|
// {
|
||||||
|
// await ctx.SendPacketAsync(new MqttPublishPacket(), TimeSpan.Zero, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
// }
|
||||||
|
// }));
|
||||||
|
|
||||||
|
// await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
|
//}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task TestLargePacket()
|
public async Task TestLargePacket()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#if NETCOREAPP
|
#if NETCOREAPP3_1
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.AspNetCore.Extensions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
|
|
||||||
@@ -31,7 +32,6 @@ namespace MQTTnet.AspNetCore.Tests
|
|||||||
result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
|
result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
|
||||||
Assert.IsFalse(result);
|
Assert.IsFalse(result);
|
||||||
|
|
||||||
|
|
||||||
part = sequence.Slice(sequence.Start, 1); // partial fixed header should fail
|
part = sequence.Slice(sequence.Start, 1); // partial fixed header should fail
|
||||||
result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
|
result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
|
||||||
Assert.IsFalse(result);
|
Assert.IsFalse(result);
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Formatter;
|
||||||
|
using MQTTnet.Tests;
|
||||||
|
|
||||||
|
namespace MQTTnet.AspNetCore.Tests
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class SpanBasedMqttPacketWriterTests : MqttPacketWriter_Tests
|
||||||
|
{
|
||||||
|
protected override IMqttPacketWriter WriterFactory()
|
||||||
|
{
|
||||||
|
return new SpanBasedMqttPacketWriter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||||
<PackageReference Include="System.IO.Pipelines" Version="4.7.1" />
|
<PackageReference Include="System.IO.Pipelines" Version="4.7.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Hosting;
|
|||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.AspNetCore.Client;
|
using MQTTnet.AspNetCore.Client;
|
||||||
|
using MQTTnet.AspNetCore.Extensions;
|
||||||
using MQTTnet.Client.Options;
|
using MQTTnet.Client.Options;
|
||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
|
namespace MQTTnet.Formatter
|
||||||
|
{
|
||||||
|
public static class MqttPacketWriterExtensions
|
||||||
|
{
|
||||||
|
public static byte[] AddMqttHeader(this IMqttPacketWriter writer, MqttControlPacketType header, byte[] body)
|
||||||
|
{
|
||||||
|
writer.Write(MqttPacketWriter.BuildFixedHeader(header));
|
||||||
|
writer.WriteVariableLengthInteger((uint)body.Length);
|
||||||
|
writer.Write(body, 0, body.Length);
|
||||||
|
return writer.GetBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Exceptions;
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
namespace MQTTnet.Tests
|
||||||
{
|
{
|
||||||
@@ -16,6 +17,18 @@ namespace MQTTnet.Tests
|
|||||||
{
|
{
|
||||||
public TestContext TestContext { get; set; }
|
public TestContext TestContext { get; set; }
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(MqttCommunicationException))]
|
||||||
|
public async Task Connect_To_Not_Existing_Server()
|
||||||
|
{
|
||||||
|
var client = new MqttFactory().CreateLowLevelMqttClient();
|
||||||
|
var options = new MqttClientOptionsBuilder()
|
||||||
|
.WithTcpServer("localhost")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await client.ConnectAsync(options, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Connect_And_Disconnect()
|
public async Task Connect_And_Disconnect()
|
||||||
{
|
{
|
||||||
@@ -78,7 +91,7 @@ namespace MQTTnet.Tests
|
|||||||
|
|
||||||
async Task<MqttConnAckPacket> Authenticate(ILowLevelMqttClient client)
|
async Task<MqttConnAckPacket> Authenticate(ILowLevelMqttClient client)
|
||||||
{
|
{
|
||||||
await client.SendAsync(new MqttConnectPacket()
|
await client.SendAsync(new MqttConnectPacket
|
||||||
{
|
{
|
||||||
CleanSession = true,
|
CleanSession = true,
|
||||||
ClientId = TestContext.TestName,
|
ClientId = TestContext.TestName,
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
|
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Client.Options;
|
using MQTTnet.Client.Options;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -11,13 +10,10 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace MQTTnet.Tests.Mockups
|
namespace MQTTnet.Tests.Mockups
|
||||||
{
|
{
|
||||||
public sealed class TestEnvironment : Disposable
|
public sealed class TestEnvironment : IDisposable
|
||||||
{
|
{
|
||||||
readonly MqttFactory _mqttFactory = new MqttFactory();
|
readonly MqttFactory _mqttFactory = new MqttFactory();
|
||||||
readonly List<IMqttClient> _clients = new List<IMqttClient>();
|
readonly List<IMqttClient> _clients = new List<IMqttClient>();
|
||||||
readonly IMqttNetLogger _serverLogger = new MqttNetLogger("server");
|
|
||||||
readonly IMqttNetLogger _clientLogger = new MqttNetLogger("client");
|
|
||||||
|
|
||||||
readonly List<string> _serverErrors = new List<string>();
|
readonly List<string> _serverErrors = new List<string>();
|
||||||
readonly List<string> _clientErrors = new List<string>();
|
readonly List<string> _clientErrors = new List<string>();
|
||||||
|
|
||||||
@@ -31,9 +27,9 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
public int ServerPort { get; set; } = 1888;
|
public int ServerPort { get; set; } = 1888;
|
||||||
|
|
||||||
public IMqttNetLogger ServerLogger => _serverLogger;
|
public IMqttNetLogger ServerLogger { get; } = new MqttNetLogger("server");
|
||||||
|
|
||||||
public IMqttNetLogger ClientLogger => _clientLogger;
|
public IMqttNetLogger ClientLogger { get; } = new MqttNetLogger("client");
|
||||||
|
|
||||||
public TestContext TestContext { get; }
|
public TestContext TestContext { get; }
|
||||||
|
|
||||||
@@ -45,7 +41,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
{
|
{
|
||||||
TestContext = testContext;
|
TestContext = testContext;
|
||||||
|
|
||||||
_serverLogger.LogMessagePublished += (s, e) =>
|
ServerLogger.LogMessagePublished += (s, e) =>
|
||||||
{
|
{
|
||||||
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
||||||
{
|
{
|
||||||
@@ -56,7 +52,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_clientLogger.LogMessagePublished += (s, e) =>
|
ClientLogger.LogMessagePublished += (s, e) =>
|
||||||
{
|
{
|
||||||
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
||||||
{
|
{
|
||||||
@@ -70,7 +66,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
public IMqttClient CreateClient()
|
public IMqttClient CreateClient()
|
||||||
{
|
{
|
||||||
var client = _mqttFactory.CreateMqttClient(_clientLogger);
|
var client = _mqttFactory.CreateMqttClient(ClientLogger);
|
||||||
_clients.Add(client);
|
_clients.Add(client);
|
||||||
|
|
||||||
return new TestClientWrapper(client, TestContext);
|
return new TestClientWrapper(client, TestContext);
|
||||||
@@ -83,13 +79,18 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
public async Task<IMqttServer> StartServerAsync(MqttServerOptionsBuilder options)
|
public async Task<IMqttServer> StartServerAsync(MqttServerOptionsBuilder options)
|
||||||
{
|
{
|
||||||
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
if (Server != null)
|
if (Server != null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Server already started.");
|
throw new InvalidOperationException("Server already started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Server = new TestServerWrapper(_mqttFactory.CreateMqttServer(_serverLogger), TestContext, this);
|
Server = new TestServerWrapper(_mqttFactory.CreateMqttServer(ServerLogger), TestContext, this);
|
||||||
await Server.StartAsync(options.WithDefaultEndpointPort(ServerPort).Build());
|
|
||||||
|
options.WithDefaultEndpointPort(ServerPort);
|
||||||
|
|
||||||
|
await Server.StartAsync(options.Build()).ConfigureAwait(false);
|
||||||
|
|
||||||
return Server;
|
return Server;
|
||||||
}
|
}
|
||||||
@@ -106,7 +107,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
options = options.WithTcpServer("localhost", ServerPort);
|
options = options.WithTcpServer("localhost", ServerPort);
|
||||||
|
|
||||||
var client = CreateClient();
|
var client = CreateClient();
|
||||||
await client.ConnectAsync(options.Build());
|
await client.ConnectAsync(options.Build()).ConfigureAwait(false);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
@@ -116,7 +117,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
var client = CreateClient();
|
var client = CreateClient();
|
||||||
await client.ConnectAsync(options);
|
await client.ConnectAsync(options).ConfigureAwait(false);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
@@ -140,26 +141,22 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (disposing)
|
foreach (var mqttClient in _clients)
|
||||||
{
|
{
|
||||||
foreach (var mqttClient in _clients)
|
mqttClient?.Dispose();
|
||||||
{
|
|
||||||
mqttClient?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Server?.StopAsync().GetAwaiter().GetResult();
|
|
||||||
|
|
||||||
ThrowIfLogErrors();
|
|
||||||
|
|
||||||
if (_exceptions.Any())
|
|
||||||
{
|
|
||||||
throw new Exception($"{_exceptions.Count} exceptions tracked.\r\n" + string.Join(Environment.NewLine, _exceptions));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(disposing);
|
Server?.StopAsync().GetAwaiter().GetResult();
|
||||||
|
Server?.Dispose();
|
||||||
|
|
||||||
|
ThrowIfLogErrors();
|
||||||
|
|
||||||
|
if (_exceptions.Any())
|
||||||
|
{
|
||||||
|
throw new Exception($"{_exceptions.Count} exceptions tracked.\r\n" + string.Join(Environment.NewLine, _exceptions));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TrackException(Exception exception)
|
public void TrackException(Exception exception)
|
||||||
|
|||||||
@@ -132,5 +132,9 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
{
|
{
|
||||||
return Implementation.UnsubscribeAsync(clientId, topicFilters);
|
return Implementation.UnsubscribeAsync(clientId, topicFilters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,38 @@ namespace MQTTnet.Tests
|
|||||||
{
|
{
|
||||||
public TestContext TestContext { get; set; }
|
public TestContext TestContext { get; set; }
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Set_ClientWasConnected_On_ServerDisconnect()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServerAsync();
|
||||||
|
var client = await testEnvironment.ConnectClientAsync();
|
||||||
|
|
||||||
|
Assert.IsTrue(client.IsConnected);
|
||||||
|
client.UseDisconnectedHandler(e => Assert.IsTrue(e.ClientWasConnected));
|
||||||
|
|
||||||
|
await server.StopAsync();
|
||||||
|
await Task.Delay(4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Set_ClientWasConnected_On_ClientDisconnect()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServerAsync();
|
||||||
|
var client = await testEnvironment.ConnectClientAsync();
|
||||||
|
|
||||||
|
Assert.IsTrue(client.IsConnected);
|
||||||
|
client.UseDisconnectedHandler(e => Assert.IsTrue(e.ClientWasConnected));
|
||||||
|
|
||||||
|
await client.DisconnectAsync();
|
||||||
|
await Task.Delay(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
||||||
public async Task Connect_To_Invalid_Server_Wrong_IP()
|
public async Task Connect_To_Invalid_Server_Wrong_IP()
|
||||||
@@ -96,7 +128,7 @@ namespace MQTTnet.Tests
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Send_Reply_In_Message_Handler()
|
public async Task Send_Reply_In_Message_Handler()
|
||||||
{
|
{
|
||||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
using (var testEnvironment = new TestEnvironment())
|
||||||
{
|
{
|
||||||
await testEnvironment.StartServerAsync();
|
await testEnvironment.StartServerAsync();
|
||||||
var client1 = await testEnvironment.ConnectClientAsync();
|
var client1 = await testEnvironment.ConnectClientAsync();
|
||||||
@@ -125,6 +157,8 @@ namespace MQTTnet.Tests
|
|||||||
|
|
||||||
await client1.PublishAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
await client1.PublishAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
SpinWait.SpinUntil(() => replyReceived, TimeSpan.FromSeconds(10));
|
SpinWait.SpinUntil(() => replyReceived, TimeSpan.FromSeconds(10));
|
||||||
|
|
||||||
Assert.IsTrue(replyReceived);
|
Assert.IsTrue(replyReceived);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Text;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Formatter.V3;
|
using MQTTnet.Formatter.V3;
|
||||||
using MQTTnet.Formatter.V5;
|
using MQTTnet.Formatter.V5;
|
||||||
@@ -18,6 +19,31 @@ namespace MQTTnet.Tests
|
|||||||
[TestClass]
|
[TestClass]
|
||||||
public class MqttPacketSerializer_Tests
|
public class MqttPacketSerializer_Tests
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void DetectVersionFromMqttConnectPacket()
|
||||||
|
{
|
||||||
|
var p = new MqttConnectPacket
|
||||||
|
{
|
||||||
|
ClientId = "XYZ",
|
||||||
|
Password = Encoding.UTF8.GetBytes("PASS"),
|
||||||
|
Username = "USER",
|
||||||
|
KeepAlivePeriod = 123,
|
||||||
|
CleanSession = true
|
||||||
|
};
|
||||||
|
var adapter = new MqttPacketFormatterAdapter(WriterFactory());
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttProtocolVersion.V310, DeserializeAndDetectVersion(adapter, Serialize(p, MqttProtocolVersion.V310)));
|
||||||
|
Assert.AreEqual(MqttProtocolVersion.V311, DeserializeAndDetectVersion(adapter, Serialize(p, MqttProtocolVersion.V311)));
|
||||||
|
Assert.AreEqual(MqttProtocolVersion.V500, DeserializeAndDetectVersion(adapter, Serialize(p, MqttProtocolVersion.V500)));
|
||||||
|
|
||||||
|
var ex = Assert.ThrowsException<MqttProtocolViolationException>(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[0])));
|
||||||
|
Assert.AreEqual("CONNECT packet must have at least 7 bytes.", ex.Message);
|
||||||
|
ex = Assert.ThrowsException<MqttProtocolViolationException>(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[7])));
|
||||||
|
Assert.AreEqual("Protocol '' not supported.", ex.Message);
|
||||||
|
ex = Assert.ThrowsException<MqttProtocolViolationException>(() => DeserializeAndDetectVersion(adapter, WriterFactory().AddMqttHeader(MqttControlPacketType.Connect, new byte[] { 255, 255, 0,0,0,0,0 })));
|
||||||
|
Assert.AreEqual("Expected at least 65537 bytes but there are only 7 bytes", ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void SerializeV310_MqttConnectPacket()
|
public void SerializeV310_MqttConnectPacket()
|
||||||
{
|
{
|
||||||
@@ -156,7 +182,7 @@ namespace MQTTnet.Tests
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Serialize_LargePacket()
|
public void Serialize_LargePacket()
|
||||||
{
|
{
|
||||||
var serializer = new MqttV311PacketFormatter(new MqttPacketWriter());
|
var serializer = new MqttV311PacketFormatter(WriterFactory());
|
||||||
|
|
||||||
const int payloadLength = 80000;
|
const int payloadLength = 80000;
|
||||||
|
|
||||||
@@ -555,25 +581,15 @@ namespace MQTTnet.Tests
|
|||||||
DeserializeAndCompare(p, "sAIAew==");
|
DeserializeAndCompare(p, "sAIAew==");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311)
|
private void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311)
|
||||||
{
|
{
|
||||||
IMqttPacketFormatter serializer;
|
Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Serialize(packet, protocolVersion)));
|
||||||
if (protocolVersion == MqttProtocolVersion.V311)
|
}
|
||||||
{
|
|
||||||
serializer = new MqttV311PacketFormatter(new MqttPacketWriter());
|
|
||||||
}
|
|
||||||
else if (protocolVersion == MqttProtocolVersion.V310)
|
|
||||||
{
|
|
||||||
serializer = new MqttV310PacketFormatter(new MqttPacketWriter());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = serializer.Encode(packet);
|
private byte[] Serialize(MqttBasePacket packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311)
|
||||||
|
{
|
||||||
Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(Join(data)));
|
var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, WriterFactory());
|
||||||
|
return Join(serializer.Encode(packet));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual IMqttPacketWriter WriterFactory()
|
protected virtual IMqttPacketWriter WriterFactory()
|
||||||
@@ -635,6 +651,25 @@ namespace MQTTnet.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter adapter, byte[] buffer)
|
||||||
|
{
|
||||||
|
using (var headerStream = new MemoryStream(buffer))
|
||||||
|
{
|
||||||
|
var channel = new TestMqttChannel(headerStream);
|
||||||
|
var fixedHeader = new byte[2];
|
||||||
|
|
||||||
|
var header = new MqttPacketReader(channel).ReadFixedHeaderAsync(fixedHeader, CancellationToken.None).GetAwaiter().GetResult().FixedHeader;
|
||||||
|
|
||||||
|
using (var bodyStream = new MemoryStream(buffer, (int)headerStream.Position, (int)header.RemainingLength))
|
||||||
|
{
|
||||||
|
var reader = ReaderFactory(bodyStream.ToArray());
|
||||||
|
var packet = new ReceivedMqttPacket(header.Flags, reader, 0);
|
||||||
|
adapter.DetectProtocolVersion(packet);
|
||||||
|
return adapter.ProtocolVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static byte[] Join(params ArraySegment<byte>[] chunks)
|
private static byte[] Join(params ArraySegment<byte>[] chunks)
|
||||||
{
|
{
|
||||||
var buffer = new MemoryStream();
|
var buffer = new MemoryStream();
|
||||||
|
|||||||
@@ -6,10 +6,15 @@ namespace MQTTnet.Tests
|
|||||||
[TestClass]
|
[TestClass]
|
||||||
public class MqttPacketWriter_Tests
|
public class MqttPacketWriter_Tests
|
||||||
{
|
{
|
||||||
|
protected virtual IMqttPacketWriter WriterFactory()
|
||||||
|
{
|
||||||
|
return new MqttPacketWriter();
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void WritePacket()
|
public void WritePacket()
|
||||||
{
|
{
|
||||||
var writer = new MqttPacketWriter();
|
var writer = WriterFactory();
|
||||||
Assert.AreEqual(0, writer.Length);
|
Assert.AreEqual(0, writer.Length);
|
||||||
|
|
||||||
writer.WriteWithLengthPrefix("1234567890");
|
writer.WriteWithLengthPrefix("1234567890");
|
||||||
|
|||||||
@@ -274,6 +274,82 @@ namespace MQTTnet.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Subscribe_Lots_In_Single_Request()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
|
{
|
||||||
|
var receivedMessagesCount = 0;
|
||||||
|
|
||||||
|
await testEnvironment.StartServerAsync();
|
||||||
|
|
||||||
|
var c1 = await testEnvironment.ConnectClientAsync();
|
||||||
|
c1.UseApplicationMessageReceivedHandler(c => Interlocked.Increment(ref receivedMessagesCount));
|
||||||
|
|
||||||
|
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
|
||||||
|
for (var i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
optionsBuilder.WithTopicFilter(i.ToString(), MqttQualityOfServiceLevel.AtMostOnce);
|
||||||
|
}
|
||||||
|
|
||||||
|
await c1.SubscribeAsync(optionsBuilder.Build()).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var c2 = await testEnvironment.ConnectClientAsync();
|
||||||
|
|
||||||
|
var messageBuilder = new MqttApplicationMessageBuilder();
|
||||||
|
for (var i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
messageBuilder.WithTopic(i.ToString());
|
||||||
|
|
||||||
|
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpinWait.SpinUntil(() => receivedMessagesCount == 500, 5000);
|
||||||
|
|
||||||
|
Assert.AreEqual(500, receivedMessagesCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Subscribe_Lots_In_Multiple_Requests()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
|
{
|
||||||
|
var receivedMessagesCount = 0;
|
||||||
|
|
||||||
|
await testEnvironment.StartServerAsync();
|
||||||
|
|
||||||
|
var c1 = await testEnvironment.ConnectClientAsync();
|
||||||
|
c1.UseApplicationMessageReceivedHandler(c => Interlocked.Increment(ref receivedMessagesCount));
|
||||||
|
|
||||||
|
for (var i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
var so = new MqttClientSubscribeOptionsBuilder()
|
||||||
|
.WithTopicFilter(i.ToString()).Build();
|
||||||
|
|
||||||
|
await c1.SubscribeAsync(so).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await Task.Delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
var c2 = await testEnvironment.ConnectClientAsync();
|
||||||
|
|
||||||
|
var messageBuilder = new MqttApplicationMessageBuilder();
|
||||||
|
for (var i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
messageBuilder.WithTopic(i.ToString());
|
||||||
|
|
||||||
|
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await Task.Delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpinWait.SpinUntil(() => receivedMessagesCount == 500, 5000);
|
||||||
|
|
||||||
|
Assert.AreEqual(500, receivedMessagesCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Subscribe_Multiple_In_Multiple_Request()
|
public async Task Subscribe_Multiple_In_Multiple_Request()
|
||||||
{
|
{
|
||||||
@@ -1312,5 +1388,95 @@ namespace MQTTnet.Tests
|
|||||||
Assert.AreEqual(expectedReceivedMessagesCount, receivedMessagesCount);
|
Assert.AreEqual(expectedReceivedMessagesCount, receivedMessagesCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Intercept_Undelivered()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment())
|
||||||
|
{
|
||||||
|
var undeliverd = string.Empty;
|
||||||
|
|
||||||
|
var options = new MqttServerOptionsBuilder().WithUndeliveredMessageInterceptor(
|
||||||
|
context =>
|
||||||
|
{
|
||||||
|
undeliverd = context.ApplicationMessage.Topic;
|
||||||
|
});
|
||||||
|
|
||||||
|
await testEnvironment.StartServerAsync(options);
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClientAsync();
|
||||||
|
|
||||||
|
await client.SubscribeAsync("b");
|
||||||
|
|
||||||
|
await client.PublishAsync("a", null, MqttQualityOfServiceLevel.ExactlyOnce);
|
||||||
|
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
Assert.AreEqual("a", undeliverd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Intercept_ClientMessageQueue()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder()
|
||||||
|
.WithClientMessageQueueInterceptor(c => c.ApplicationMessage.Topic = "a"));
|
||||||
|
|
||||||
|
var topicAReceived = false;
|
||||||
|
var topicBReceived = false;
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClientAsync();
|
||||||
|
client.UseApplicationMessageReceivedHandler(c =>
|
||||||
|
{
|
||||||
|
if (c.ApplicationMessage.Topic == "a")
|
||||||
|
{
|
||||||
|
topicAReceived = true;
|
||||||
|
}
|
||||||
|
else if (c.ApplicationMessage.Topic == "b")
|
||||||
|
{
|
||||||
|
topicBReceived = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await client.SubscribeAsync("a");
|
||||||
|
await client.SubscribeAsync("b");
|
||||||
|
|
||||||
|
await client.PublishAsync("b");
|
||||||
|
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
Assert.IsTrue(topicAReceived);
|
||||||
|
Assert.IsFalse(topicBReceived);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Intercept_ClientMessageQueue_Different_QoS_Of_Subscription_And_Message()
|
||||||
|
{
|
||||||
|
const string topic = "a";
|
||||||
|
|
||||||
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder()
|
||||||
|
.WithClientMessageQueueInterceptor(c => { })); // Interceptor does nothing but has to be present.
|
||||||
|
|
||||||
|
bool receivedMessage = false;
|
||||||
|
var client = await testEnvironment.ConnectClientAsync();
|
||||||
|
client.UseApplicationMessageReceivedHandler(c =>
|
||||||
|
{
|
||||||
|
receivedMessage = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await client.PublishAsync(new MqttApplicationMessage{ Topic = topic, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
|
||||||
|
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
Assert.IsTrue(receivedMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Client.Options;
|
||||||
using MQTTnet.Client.Subscribing;
|
using MQTTnet.Client.Subscribing;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
@@ -84,5 +85,40 @@ namespace MQTTnet.Tests
|
|||||||
Assert.AreEqual(true, session.Items["can_subscribe_x"]);
|
Assert.AreEqual(true, session.Items["can_subscribe_x"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Manage_Session_MaxParallel()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
|
{
|
||||||
|
testEnvironment.IgnoreClientLogErrors = true;
|
||||||
|
var serverOptions = new MqttServerOptionsBuilder();
|
||||||
|
await testEnvironment.StartServerAsync(serverOptions);
|
||||||
|
|
||||||
|
var options = new MqttClientOptionsBuilder()
|
||||||
|
.WithClientId("1")
|
||||||
|
;
|
||||||
|
|
||||||
|
var clients = await Task.WhenAll(Enumerable.Range(0, 10)
|
||||||
|
.Select(i => TryConnect(testEnvironment, options)));
|
||||||
|
|
||||||
|
var connectedClients = clients.Where(c => c?.IsConnected ?? false).ToList();
|
||||||
|
|
||||||
|
Assert.AreEqual(1, connectedClients.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IMqttClient> TryConnect(TestEnvironment testEnvironment, MqttClientOptionsBuilder options)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await testEnvironment.ConnectClientAsync(options);
|
||||||
|
}
|
||||||
|
catch (System.Exception)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<Router AppAssembly="@typeof(Program).Assembly">
|
||||||
|
<Found Context="routeData">
|
||||||
|
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||||
|
</Found>
|
||||||
|
<NotFound>
|
||||||
|
<LayoutView Layout="@typeof(MainLayout)">
|
||||||
|
<p>Sorry, there's nothing at this address.</p>
|
||||||
|
</LayoutView>
|
||||||
|
</NotFound>
|
||||||
|
</Router>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<RazorLangVersion>3.0</RazorLangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.0" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="System.Net.Http.Json" Version="3.2.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\Source\MQTTnet\MQTTnet.csproj" />
|
||||||
|
<ProjectReference Include="..\Shared\MQTTnet.Test.BlazorApp.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
@page "/counter"
|
||||||
|
@using MQTTnet.Client.Options
|
||||||
|
@using System.Threading
|
||||||
|
|
||||||
|
<h1>Counter</h1>
|
||||||
|
|
||||||
|
<p>Current count: @currentCount</p>
|
||||||
|
|
||||||
|
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private int currentCount = 0;
|
||||||
|
|
||||||
|
private async Task IncrementCount()
|
||||||
|
{
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
var client = mqttFactory.CreateMqttClient();
|
||||||
|
|
||||||
|
var options = new MqttClientOptionsBuilder()
|
||||||
|
.WithWebSocketServer("192.168.1.15:80/mqtt")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await client.ConnectAsync(options, CancellationToken.None);
|
||||||
|
|
||||||
|
currentCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
@page "/fetchdata"
|
||||||
|
@using MQTTnet.Test.BlazorApp.Shared
|
||||||
|
@inject HttpClient Http
|
||||||
|
|
||||||
|
<h1>Weather forecast</h1>
|
||||||
|
|
||||||
|
<p>This component demonstrates fetching data from the server.</p>
|
||||||
|
|
||||||
|
@if (forecasts == null)
|
||||||
|
{
|
||||||
|
<p><em>Loading...</em></p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Temp. (C)</th>
|
||||||
|
<th>Temp. (F)</th>
|
||||||
|
<th>Summary</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var forecast in forecasts)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@forecast.Date.ToShortDateString()</td>
|
||||||
|
<td>@forecast.TemperatureC</td>
|
||||||
|
<td>@forecast.TemperatureF</td>
|
||||||
|
<td>@forecast.Summary</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private WeatherForecast[] forecasts;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
@page "/"
|
||||||
|
|
||||||
|
<h1>Hello, world!</h1>
|
||||||
|
|
||||||
|
Welcome to your new app.
|
||||||
|
|
||||||
|
<SurveyPrompt Title="How is Blazor working for you?" />
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace MQTTnet.Test.BlazorApp.Client
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
public static Task Main(string[] args)
|
||||||
|
{
|
||||||
|
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||||
|
builder.RootComponents.Add<App>("app");
|
||||||
|
|
||||||
|
builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||||
|
|
||||||
|
return builder.Build().RunAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
|
<div class="sidebar">
|
||||||
|
<NavMenu />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main">
|
||||||
|
<div class="top-row px-4">
|
||||||
|
<a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content px-4">
|
||||||
|
@Body
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<div class="top-row pl-4 navbar navbar-dark">
|
||||||
|
<a class="navbar-brand" href="">MQTTnet.Test.BlazorApp</a>
|
||||||
|
<button class="navbar-toggler" @onclick="ToggleNavMenu">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
|
||||||
|
<ul class="nav flex-column">
|
||||||
|
<li class="nav-item px-3">
|
||||||
|
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||||
|
<span class="oi oi-home" aria-hidden="true"></span> Home
|
||||||
|
</NavLink>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item px-3">
|
||||||
|
<NavLink class="nav-link" href="counter">
|
||||||
|
<span class="oi oi-plus" aria-hidden="true"></span> Counter
|
||||||
|
</NavLink>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item px-3">
|
||||||
|
<NavLink class="nav-link" href="fetchdata">
|
||||||
|
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
|
||||||
|
</NavLink>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private bool collapseNavMenu = true;
|
||||||
|
|
||||||
|
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
|
||||||
|
|
||||||
|
private void ToggleNavMenu()
|
||||||
|
{
|
||||||
|
collapseNavMenu = !collapseNavMenu;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<div class="alert alert-secondary mt-4" role="alert">
|
||||||
|
<span class="oi oi-pencil mr-2" aria-hidden="true"></span>
|
||||||
|
<strong>@Title</strong>
|
||||||
|
|
||||||
|
<span class="text-nowrap">
|
||||||
|
Please take our
|
||||||
|
<a target="_blank" class="font-weight-bold" href="https://go.microsoft.com/fwlink/?linkid=2127996">brief survey</a>
|
||||||
|
</span>
|
||||||
|
and tell us what you think.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
// Demonstrates how a parent component can supply parameters
|
||||||
|
[Parameter]
|
||||||
|
public string Title { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
@using System.Net.Http
|
||||||
|
@using System.Net.Http.Json
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using Microsoft.AspNetCore.Components.WebAssembly.Http
|
||||||
|
@using Microsoft.JSInterop
|
||||||
|
@using MQTTnet.Test.BlazorApp.Client
|
||||||
|
@using MQTTnet.Test.BlazorApp.Client.Shared
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, .btn-link {
|
||||||
|
color: #0366d6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1b6ec2;
|
||||||
|
border-color: #1861ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
app {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row {
|
||||||
|
height: 3.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .top-row {
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border-bottom: 1px solid #d6d5d5;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .top-row > a, .main .top-row .btn-link {
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .top-row a:first-child {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .top-row {
|
||||||
|
background-color: rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .navbar-brand {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .oi {
|
||||||
|
width: 2rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
vertical-align: text-top;
|
||||||
|
top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-item {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-item:first-of-type {
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-item:last-of-type {
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-item a {
|
||||||
|
color: #d7d7d7;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 3rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-item a.active {
|
||||||
|
background-color: rgba(255,255,255,0.25);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-item a:hover {
|
||||||
|
background-color: rgba(255,255,255,0.1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding-top: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-toggler {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.valid.modified:not([type=checkbox]) {
|
||||||
|
outline: 1px solid #26b050;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invalid {
|
||||||
|
outline: 1px solid red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.validation-message {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#blazor-error-ui {
|
||||||
|
background: lightyellow;
|
||||||
|
bottom: 0;
|
||||||
|
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
display: none;
|
||||||
|
left: 0;
|
||||||
|
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#blazor-error-ui .dismiss {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
right: 0.75rem;
|
||||||
|
top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.main .top-row:not(.auth) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .top-row.auth {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .top-row a, .main .top-row .btn-link {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
app {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 250px;
|
||||||
|
height: 100vh;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main .top-row {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main > div {
|
||||||
|
padding-left: 2rem !important;
|
||||||
|
padding-right: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-toggler {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .collapse {
|
||||||
|
/* Never collapse the sidebar for wide screens */
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user