mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 17:00:03 +08:00
Compare commits
57
Commits
v3.0.0-alpha3
...
v3.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efef23e3f5 | ||
|
|
53ab2ac140 | ||
|
|
2ecbd83d06 | ||
|
|
a278b6189b | ||
|
|
cdbdfdec6d | ||
|
|
808c2da5f6 | ||
|
|
6ed3b0cedd | ||
|
|
9da3377aa7 | ||
|
|
0cac2aa1bc | ||
|
|
2d59b5c1ac | ||
|
|
9dfa0b32e4 | ||
|
|
20a7579156 | ||
|
|
c7409c2ff4 | ||
|
|
d4c6fb44c9 | ||
|
|
e6cf23aebd | ||
|
|
0fec860b25 | ||
|
|
2c927c5995 | ||
|
|
6399172582 | ||
|
|
5c903e6fbf | ||
|
|
f60bbf7dd7 | ||
|
|
519bb05aac | ||
|
|
a8434d1b58 | ||
|
|
420b35e37d | ||
|
|
a25c8cd4c7 | ||
|
|
8ac77558af | ||
|
|
953578eba2 | ||
|
|
527713ccf8 | ||
|
|
477e2d23ab | ||
|
|
03c61ad82c | ||
|
|
9475ccd0d8 | ||
|
|
73b051d159 | ||
|
|
a9944def68 | ||
|
|
aee10ba6fa | ||
|
|
cfc1b27950 | ||
|
|
149d01e173 | ||
|
|
888e1797d7 | ||
|
|
f8ade2daf4 | ||
|
|
a85792c392 | ||
|
|
fbca8a6dc1 | ||
|
|
732ea84caa | ||
|
|
65896269da | ||
|
|
631f660d5c | ||
|
|
7f08371532 | ||
|
|
1afd59325f | ||
|
|
45c033f401 | ||
|
|
7855eccb78 | ||
|
|
d51300d32d | ||
|
|
61b10a8b56 | ||
|
|
dbee941dcc | ||
|
|
fd7c98d0d1 | ||
|
|
c911797f1b | ||
|
|
b5a1d3a69f | ||
|
|
cdeb073bb0 | ||
|
|
a8db0dd3d6 | ||
|
|
2267770a72 | ||
|
|
35ee732045 | ||
|
|
ad5834dd1f |
@@ -288,8 +288,6 @@ __pycache__/
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
|
||||
Build/nuget.exe
|
||||
*.js
|
||||
*.map
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
@@ -17,12 +17,16 @@
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
|
||||
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.3" />
|
||||
<dependency id="Microsoft.AspNetCore.Http.Connections" version="1.0.3" />
|
||||
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.1.1" />
|
||||
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.1.1" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
|
||||
</files>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
@@ -19,6 +19,9 @@
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
@@ -19,6 +19,9 @@
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
@@ -30,7 +30,13 @@
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
|
||||
</files>
|
||||
</package>
|
||||
+12
-4
@@ -5,23 +5,27 @@
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
||||
<releaseNotes> * [Core] Added support for MQTTv5 packages.
|
||||
* [Core] Performance improvements (removed several exceptions).
|
||||
<releaseNotes>
|
||||
* [Core] Added support for MQTTv5 packages.
|
||||
* [Core] Performance improvements.
|
||||
* [Core] Removed obsolete methods.
|
||||
* [Core] Fixed a memory leak when processing lots of messages (thanks to @tschanko)
|
||||
* [Core] Added more overloads for MQTT factory.
|
||||
* [Core] The client password is now hidden from the logs (replaced with **** if set).
|
||||
* [Core] Fixed a memory leak when using SSL connections (thanks to @biovoid).
|
||||
* [Client] Added validation of topics before publishing.
|
||||
* [Client] Added new MQTTv5 features to options builder.
|
||||
* [Client] Added uniform API across all supported MQTT versions (BREAKING CHANGE!)
|
||||
* [Client] The client will now avoid sending an ACK if an exception has been thrown in message handler (thanks to @ramonsmits).
|
||||
* [Client] Fixed issues in QoS 2 handling which leads to message loss.
|
||||
* [Client] Replaced all events with proper async compatible handlers (BREAKING CHANGE!).
|
||||
* [Client] Added more configuration values to TCP endpoint options.
|
||||
* [Client] Added used PacketIdentifier to publish result.
|
||||
* [ManagedClient] Replaced all events with proper async compatible handlers (BREAKING CHANGE!).
|
||||
* [ManagedClient] The log ID is now propagated to the internal client (thanks to @vbBerni).
|
||||
* [ManagedClient] Added validation of topics before publishing.
|
||||
@@ -33,6 +37,7 @@
|
||||
* [Server] Added the flag "IsSecureConnection" which is set to true when the connection is encrypted.
|
||||
* [Server] Fixed wrong will message behavior when stopping server (thanks to @JohBa)
|
||||
* [Server] Added validation of topics before publishing.
|
||||
* [Server] Added more configuration values to TCP endpoint options.
|
||||
* [MQTTnet Server] Added as first Alpha version of standalone cross platform MQTT server.
|
||||
|
||||
* [Note] Due to MQTTv5 a lot of new classes were introduced. This required adding new namespaces as well. Most classes are backward compatible but new namespaces must be added.
|
||||
@@ -53,7 +58,7 @@
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="uap10.0">
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.9" />
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.8" />
|
||||
</group>
|
||||
<group targetFramework="net452">
|
||||
</group>
|
||||
@@ -63,6 +68,9 @@
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
|
||||
+53
-44
@@ -3,57 +3,66 @@ param([string]$assemblyVersion, [string]$nugetVersion)
|
||||
if ([string]::IsNullOrEmpty($assemblyVersion)) {$assemblyVersion = "0.0.1"}
|
||||
if ([string]::IsNullOrEmpty($nugetVersion)) {$nugetVersion = "0.0.1"}
|
||||
|
||||
$vswhere = ${Env:\ProgramFiles(x86)} + '\Microsoft Visual Studio\Installer\vswhere'
|
||||
$msbuild = &$vswhere -products * -requires Microsoft.Component.MSBuild -latest -find MSBuild\**\Bin\MSBuild.exe
|
||||
|
||||
Write-Host
|
||||
Write-Host "Assembly version = $assemblyVersion"
|
||||
Write-Host "Nuget version = $nugetVersion"
|
||||
Write-Host "MSBuild path = $msbuild"
|
||||
Write-Host
|
||||
|
||||
$vswhere = ${Env:\ProgramFiles(x86)} + '\Microsoft Visual Studio\Installer\vswhere'
|
||||
# Build the core library
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /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="net461" /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="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
$path = &$vswhere -latest -products * -requires Microsoft.Component.MSBuild -property installationPath
|
||||
if ($path) {
|
||||
$msbuild = join-path $path 'MSBuild\15.0\Bin\MSBuild.exe'
|
||||
# Build the ASP.NET Core 2.0 extension
|
||||
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.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"
|
||||
|
||||
# Build the core library
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /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="net461" /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="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
|
||||
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.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"
|
||||
# Build the RPC extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.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.Extensions.Rpc\MQTTnet.Extensions.Rpc.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.Extensions.Rpc\MQTTnet.Extensions.Rpc.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 RPC extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.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.Extensions.Rpc\MQTTnet.Extensions.Rpc.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.Extensions.Rpc\MQTTnet.Extensions.Rpc.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 Managed Client extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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 Managed Client extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.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 MQTTnet.Server
|
||||
Remove-Item ..\Source\MQTTnet.Server\bin\Release\netcoreapp2.2 -Recurse -Force -ErrorAction SilentlyContinue
|
||||
&$msbuild ..\Source\MQTTnet.Server\MQTTnet.Server.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp2.2" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
|
||||
$source = "..\Source\MQTTnet.Server\bin\Release\netcoreapp2.2"
|
||||
$destination = "..\Source\MQTTnet.Server\bin\MQTTnet.Server-Portable-v$nugetVersion.zip"
|
||||
If(Test-path $destination) {Remove-item $destination}
|
||||
Add-Type -assembly "system.io.compression.filesystem"
|
||||
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
|
||||
|
||||
Copy-Item MQTTnet.AspNetCore.nuspec -Destination MQTTnet.AspNetCore.nuspec.old -Force
|
||||
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
|
||||
Copy-Item MQTTnet.Extensions.Rpc.nuspec -Destination MQTTnet.Extensions.Rpc.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.Rpc.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.Rpc.nuspec
|
||||
Copy-Item MQTTnet.Extensions.ManagedClient.nuspec -Destination MQTTnet.Extensions.ManagedClient.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.ManagedClient.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.ManagedClient.nuspec
|
||||
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.NETStandard.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
|
||||
Move-Item MQTTnet.AspNetCore.nuspec.old -Destination MQTTnet.AspNetCore.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.Rpc.nuspec.old -Destination MQTTnet.Extensions.Rpc.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.ManagedClient.nuspec.old -Destination MQTTnet.Extensions.ManagedClient.nuspec -Force
|
||||
}
|
||||
# Create NuGet packages.
|
||||
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
Copy-Item MQTTnet.AspNetCore.nuspec -Destination MQTTnet.AspNetCore.nuspec.old -Force
|
||||
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
|
||||
Copy-Item MQTTnet.Extensions.Rpc.nuspec -Destination MQTTnet.Extensions.Rpc.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.Rpc.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.Rpc.nuspec
|
||||
Copy-Item MQTTnet.Extensions.ManagedClient.nuspec -Destination MQTTnet.Extensions.ManagedClient.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.ManagedClient.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.ManagedClient.nuspec
|
||||
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.NETStandard.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
|
||||
Move-Item MQTTnet.AspNetCore.nuspec.old -Destination MQTTnet.AspNetCore.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.Rpc.nuspec.old -Destination MQTTnet.Extensions.Rpc.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.ManagedClient.nuspec.old -Destination MQTTnet.Extensions.ManagedClient.nuspec -Force
|
||||
Binary file not shown.
+3
-2
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.28729.10
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
EndProject
|
||||
@@ -19,6 +19,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1
|
||||
Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
|
||||
Build\MQTTnet.Extensions.ManagedClient.nuspec = Build\MQTTnet.Extensions.ManagedClient.nuspec
|
||||
Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
|
||||
Build\MQTTnet.NETStandard.nuspec = Build\MQTTnet.NETStandard.nuspec
|
||||
Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
|
||||
Build\upload.ps1 = Build\upload.ps1
|
||||
EndProjectSection
|
||||
|
||||
@@ -26,7 +26,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
* Uniform API across all supported versions of the MQTT protocol
|
||||
* Interfaces included for mocking and testing
|
||||
* Access to internal trace messages
|
||||
* Unit tested (~150 tests)
|
||||
* Unit tested (~200 tests)
|
||||
* No external dependencies
|
||||
|
||||
\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
|
||||
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
|
||||
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol))
|
||||
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol).ConfigureAwait(false))
|
||||
{
|
||||
await adapter.RunWebSocketConnectionAsync(webSocket, context);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,10 @@ namespace MQTTnet.AspNetCore.Client
|
||||
{
|
||||
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
|
||||
var tcpConnection = new TcpConnection(endpoint);
|
||||
return new MqttConnectionContext(new MqttPacketFormatterAdapter(options.ProtocolVersion), tcpConnection);
|
||||
|
||||
var writer = new SpanBasedMqttPacketWriter();
|
||||
var formatter = new MqttPacketFormatterAdapter(options.ProtocolVersion, writer);
|
||||
return new MqttConnectionContext(formatter, tcpConnection);
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
||||
@@ -5,6 +5,10 @@ using System.Diagnostics;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.Sockets;
|
||||
|
||||
#if NETCOREAPP2_1
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public class SocketSender
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<TargetFrameworks>netstandard2.0;netcoreapp2.1</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
<PackageId />
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
@@ -15,6 +16,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Connections.Features;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore.Client.Tcp;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using System;
|
||||
using System.IO.Pipelines;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
@@ -17,30 +18,64 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||
|
||||
if (Connection.Transport != null)
|
||||
{
|
||||
_input = Connection.Transport.Input;
|
||||
_output = Connection.Transport.Output;
|
||||
}
|
||||
|
||||
_reader = new SpanBasedMqttPacketBodyReader();
|
||||
}
|
||||
|
||||
public string Endpoint => Connection.ConnectionId;
|
||||
public bool IsSecureConnection => false; // TODO: Fix detection.
|
||||
private PipeReader _input;
|
||||
private PipeWriter _output;
|
||||
private readonly SpanBasedMqttPacketBodyReader _reader;
|
||||
|
||||
public string Endpoint
|
||||
{
|
||||
get
|
||||
{
|
||||
var connection = Http?.HttpContext?.Connection;
|
||||
if (connection == null)
|
||||
{
|
||||
return Connection.ConnectionId;
|
||||
}
|
||||
|
||||
return $"{connection.RemoteIpAddress}:{connection.RemotePort}";
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSecureConnection => Http?.HttpContext?.Request?.IsHttps ?? false;
|
||||
|
||||
private IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
|
||||
|
||||
public ConnectionContext Connection { get; }
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
public event EventHandler ReadingPacketStarted;
|
||||
public event EventHandler ReadingPacketCompleted;
|
||||
|
||||
public long BytesSent { get; set; }
|
||||
public long BytesReceived { get; set; }
|
||||
|
||||
public Action ReadingPacketStartedCallback { get; set; }
|
||||
public Action ReadingPacketCompletedCallback { get; set; }
|
||||
|
||||
private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Connection is TcpConnection tcp && !tcp.IsConnected)
|
||||
{
|
||||
return tcp.StartAsync();
|
||||
await tcp.StartAsync().ConfigureAwait(false);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
|
||||
_input = Connection.Transport.Input;
|
||||
_output = Connection.Transport.Output;
|
||||
}
|
||||
|
||||
public Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
Connection.Transport.Input.Complete();
|
||||
Connection.Transport.Output.Complete();
|
||||
_input?.Complete();
|
||||
_output?.Complete();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -73,14 +108,15 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed))
|
||||
if (PacketFormatterAdapter.TryDecode(_reader, buffer, out var packet, out consumed, out observed, out var received))
|
||||
{
|
||||
BytesReceived += received;
|
||||
return packet;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we did receive something but the message is not yet complete
|
||||
ReadingPacketStarted?.Invoke(this, EventArgs.Empty);
|
||||
ReadingPacketStartedCallback?.Invoke();
|
||||
}
|
||||
}
|
||||
else if (readResult.IsCompleted)
|
||||
@@ -99,7 +135,7 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReadingPacketCompleted?.Invoke(this, EventArgs.Empty);
|
||||
ReadingPacketCompletedCallback?.Invoke();
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -108,13 +144,20 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
var buffer = PacketFormatterAdapter.Encode(packet).AsMemory();
|
||||
var output = Connection.Transport.Output;
|
||||
var formatter = PacketFormatterAdapter;
|
||||
|
||||
|
||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await output.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
|
||||
var buffer = formatter.Encode(packet);
|
||||
var msg = buffer.AsMemory();
|
||||
var output = _output;
|
||||
msg.CopyTo(output.GetMemory(msg.Length));
|
||||
BytesSent += msg.Length;
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
output.Advance(msg.Length);
|
||||
await output.FlushAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
|
||||
{
|
||||
public Action<MqttServerAdapterClientAcceptedEventArgs> ClientAcceptedHandler { get; set; }
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||
{
|
||||
@@ -21,12 +21,15 @@ namespace MQTTnet.AspNetCore
|
||||
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
|
||||
}
|
||||
|
||||
using (var adapter = new MqttConnectionContext(new MqttPacketFormatterAdapter(), connection))
|
||||
var writer = new SpanBasedMqttPacketWriter();
|
||||
var formatter = new MqttPacketFormatterAdapter(writer);
|
||||
using (var adapter = new MqttConnectionContext(formatter, connection))
|
||||
{
|
||||
var args = new MqttServerAdapterClientAcceptedEventArgs(adapter);
|
||||
ClientAcceptedHandler?.Invoke(args);
|
||||
|
||||
await args.SessionTask.ConfigureAwait(false);
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
await clientHandler(adapter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace MQTTnet.AspNetCore
|
||||
_logger = logger.CreateChildLogger(nameof(MqttTcpServerAdapter));
|
||||
}
|
||||
|
||||
public Action<MqttServerAdapterClientAcceptedEventArgs> ClientAcceptedHandler { get; set; }
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public Task StartAsync(IMqttServerOptions options)
|
||||
{
|
||||
@@ -43,15 +43,16 @@ namespace MQTTnet.AspNetCore
|
||||
var isSecureConnection = clientCertificate != null;
|
||||
clientCertificate?.Dispose();
|
||||
|
||||
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection);
|
||||
var clientAdapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(), _logger.CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
|
||||
|
||||
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
|
||||
ClientAcceptedHandler?.Invoke(eventArgs);
|
||||
|
||||
if (eventArgs.SessionTask != null)
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
await eventArgs.SessionTask.ConfigureAwait(false);
|
||||
var writer = new SpanBasedMqttPacketWriter();
|
||||
var formatter = new MqttPacketFormatterAdapter(writer);
|
||||
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection);
|
||||
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger.CreateChildLogger(nameof(MqttWebSocketServerAdapter))))
|
||||
{
|
||||
await clientHandler(channelAdapter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,13 +9,20 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public static class ReaderExtensions
|
||||
{
|
||||
public static bool TryDecode(this MqttPacketFormatterAdapter formatter, in ReadOnlySequence<byte> input, out MqttBasePacket packet, out SequencePosition consumed, out SequencePosition observed)
|
||||
public static bool TryDecode(this MqttPacketFormatterAdapter formatter,
|
||||
SpanBasedMqttPacketBodyReader reader,
|
||||
in ReadOnlySequence<byte> input,
|
||||
out MqttBasePacket packet,
|
||||
out SequencePosition consumed,
|
||||
out SequencePosition observed,
|
||||
out int bytesRead)
|
||||
{
|
||||
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
|
||||
|
||||
packet = null;
|
||||
consumed = input.Start;
|
||||
observed = input.End;
|
||||
bytesRead = 0;
|
||||
var copy = input;
|
||||
|
||||
if (copy.Length < 2)
|
||||
@@ -24,7 +31,7 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
|
||||
var fixedheader = copy.First.Span[0];
|
||||
if (!TryReadBodyLength(ref copy, out var bodyLength))
|
||||
if (!TryReadBodyLength(ref copy, out int headerLength, out var bodyLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -35,34 +42,45 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
|
||||
var bodySlice = copy.Slice(0, bodyLength);
|
||||
var buffer = bodySlice.GetArray();
|
||||
packet = formatter.Decode(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length), buffer.Length + 2));
|
||||
var buffer = bodySlice.GetMemory();
|
||||
reader.SetBuffer(buffer);
|
||||
|
||||
var receivedMqttPacket = new ReceivedMqttPacket(fixedheader, reader, buffer.Length + 2);
|
||||
|
||||
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
formatter.DetectProtocolVersion(receivedMqttPacket);
|
||||
}
|
||||
|
||||
packet = formatter.Decode(receivedMqttPacket);
|
||||
consumed = bodySlice.End;
|
||||
observed = bodySlice.End;
|
||||
bytesRead = headerLength + bodyLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static byte[] GetArray(this in ReadOnlySequence<byte> input)
|
||||
private static ReadOnlyMemory<byte> GetMemory(this in ReadOnlySequence<byte> input)
|
||||
{
|
||||
if (input.IsSingleSegment)
|
||||
{
|
||||
return input.First.Span.ToArray();
|
||||
return input.First;
|
||||
}
|
||||
|
||||
// Should be rare
|
||||
return input.ToArray();
|
||||
}
|
||||
|
||||
private static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int result)
|
||||
private static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int headerLength, out int bodyLength)
|
||||
{
|
||||
// Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
|
||||
var multiplier = 1;
|
||||
var value = 0;
|
||||
byte encodedByte;
|
||||
var index = 1;
|
||||
result = 0;
|
||||
|
||||
var temp = input.Slice(0, Math.Min(5, input.Length)).GetArray();
|
||||
headerLength = 0;
|
||||
bodyLength = 0;
|
||||
|
||||
var temp = input.Slice(0, Math.Min(5, input.Length)).GetMemory();
|
||||
|
||||
do
|
||||
{
|
||||
@@ -70,13 +88,13 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
return false;
|
||||
}
|
||||
encodedByte = temp[index];
|
||||
encodedByte = temp.Span[index];
|
||||
index++;
|
||||
|
||||
value += (byte)(encodedByte & 127) * multiplier;
|
||||
if (multiplier > 128 * 128 * 128)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", temp.AsSpan(1, index).ToArray())}).");
|
||||
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", temp.Slice(1, index).ToArray())}).");
|
||||
}
|
||||
|
||||
multiplier *= 128;
|
||||
@@ -84,7 +102,8 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
input = input.Slice(index);
|
||||
|
||||
result = value;
|
||||
headerLength = index;
|
||||
bodyLength = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class SpanBasedMqttPacketBodyReader : IMqttPacketBodyReader
|
||||
{
|
||||
private ReadOnlyMemory<byte> _buffer;
|
||||
private int _offset;
|
||||
|
||||
public void SetBuffer(ReadOnlyMemory<byte> buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_offset = 0;
|
||||
}
|
||||
|
||||
public ulong Length => (ulong)_buffer.Length;
|
||||
|
||||
public bool EndOfStream => _buffer.Length.Equals(_offset);
|
||||
|
||||
public ulong Offset => (ulong)_offset;
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
return _buffer.Span[_offset++];
|
||||
}
|
||||
|
||||
public byte[] ReadRemainingData()
|
||||
{
|
||||
return _buffer.Slice(_offset).ToArray();
|
||||
}
|
||||
|
||||
public byte[] ReadWithLengthPrefix()
|
||||
{
|
||||
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()
|
||||
{
|
||||
var buffer = ReadSegmentWithLengthPrefix();
|
||||
if (buffer.Length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
fixed (byte* bytes = &buffer.GetPinnableReference())
|
||||
{
|
||||
var result = Encoding.UTF8.GetString(bytes, buffer.Length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort ReadTwoByteInteger()
|
||||
{
|
||||
var result = BinaryPrimitives.ReadUInt16BigEndian(_buffer.Span.Slice(_offset));
|
||||
_offset += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
public uint ReadFourByteInteger()
|
||||
{
|
||||
var result = BinaryPrimitives.ReadUInt32BigEndian(_buffer.Span.Slice(_offset));
|
||||
_offset += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
public uint ReadVariableLengthInteger()
|
||||
{
|
||||
var multiplier = 1;
|
||||
var value = 0U;
|
||||
byte encodedByte;
|
||||
|
||||
do
|
||||
{
|
||||
encodedByte = ReadByte();
|
||||
value += (uint)((encodedByte & 127) * multiplier);
|
||||
|
||||
if (multiplier > 2097152)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Variable length integer is invalid.");
|
||||
}
|
||||
|
||||
multiplier *= 128;
|
||||
} while ((encodedByte & 128) != 0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool ReadBoolean()
|
||||
{
|
||||
var buffer = ReadByte();
|
||||
|
||||
if (buffer == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new MqttProtocolViolationException("Boolean values can be 0 or 1 only.");
|
||||
}
|
||||
|
||||
public void Seek(ulong position)
|
||||
{
|
||||
_offset = (int)position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
using MQTTnet.Formatter;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class SpanBasedMqttPacketWriter : IMqttPacketWriter
|
||||
{
|
||||
private readonly ArrayPool<byte> _pool;
|
||||
|
||||
public SpanBasedMqttPacketWriter()
|
||||
{
|
||||
_pool = ArrayPool<byte>.Create();
|
||||
|
||||
}
|
||||
|
||||
private byte[] _buffer;
|
||||
private int _position;
|
||||
|
||||
public int Length { get; set; }
|
||||
|
||||
public void FreeBuffer()
|
||||
{
|
||||
_pool.Return(_buffer);
|
||||
}
|
||||
|
||||
public byte[] GetBuffer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public void Reset(int v)
|
||||
{
|
||||
_buffer = _pool.Rent(1500);
|
||||
Length = v;
|
||||
_position = v;
|
||||
}
|
||||
|
||||
public void Seek(int v)
|
||||
{
|
||||
_position = v;
|
||||
}
|
||||
|
||||
public void Write(byte value)
|
||||
{
|
||||
GrowIfNeeded(1);
|
||||
_buffer[_position] = value;
|
||||
Commit(1);
|
||||
}
|
||||
|
||||
public void Write(ushort value)
|
||||
{
|
||||
GrowIfNeeded(2);
|
||||
|
||||
BinaryPrimitives.WriteUInt16BigEndian(_buffer.AsSpan(_position), value);
|
||||
Commit(2);
|
||||
}
|
||||
|
||||
public void Write(IMqttPacketWriter propertyWriter)
|
||||
{
|
||||
if (propertyWriter == null) throw new ArgumentNullException(nameof(propertyWriter));
|
||||
|
||||
GrowIfNeeded(propertyWriter.Length);
|
||||
Write(propertyWriter.GetBuffer(), 0, propertyWriter.Length);
|
||||
Commit(propertyWriter.Length);
|
||||
}
|
||||
|
||||
public void Write(byte[] payload, int start, int length)
|
||||
{
|
||||
GrowIfNeeded(length);
|
||||
|
||||
payload.AsSpan(start, length).CopyTo(_buffer.AsSpan(_position));
|
||||
Commit(length);
|
||||
}
|
||||
|
||||
public void WriteVariableLengthInteger(uint value)
|
||||
{
|
||||
GrowIfNeeded(4);
|
||||
|
||||
var x = value;
|
||||
do
|
||||
{
|
||||
var encodedByte = x % 128;
|
||||
x = x / 128;
|
||||
if (x > 0)
|
||||
{
|
||||
encodedByte = encodedByte | 128;
|
||||
}
|
||||
|
||||
_buffer[_position] = (byte)encodedByte;
|
||||
Commit(1);
|
||||
} while (x > 0);
|
||||
}
|
||||
|
||||
public void WriteWithLengthPrefix(string value)
|
||||
{
|
||||
var bytesLength = Encoding.UTF8.GetByteCount(value ?? string.Empty);
|
||||
GrowIfNeeded(bytesLength + 2);
|
||||
|
||||
Write((ushort)bytesLength);
|
||||
Encoding.UTF8.GetBytes(value ?? string.Empty, 0, value?.Length ?? 0, _buffer, _position);
|
||||
Commit(bytesLength);
|
||||
}
|
||||
|
||||
public void WriteWithLengthPrefix(byte[] payload)
|
||||
{
|
||||
GrowIfNeeded(payload.Length + 2);
|
||||
|
||||
Write((ushort)payload.Length);
|
||||
payload.CopyTo(_buffer, _position);
|
||||
Commit(payload.Length);
|
||||
}
|
||||
|
||||
private void Commit(int count)
|
||||
{
|
||||
if (_position == Length)
|
||||
{
|
||||
Length += count;
|
||||
}
|
||||
|
||||
_position += count;
|
||||
}
|
||||
|
||||
private void GrowIfNeeded(int requiredAdditional)
|
||||
{
|
||||
var requiredTotal = _position + requiredAdditional;
|
||||
if (_buffer.Length >= requiredTotal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newBufferLength = _buffer.Length;
|
||||
while (newBufferLength < requiredTotal)
|
||||
{
|
||||
newBufferLength *= 2;
|
||||
}
|
||||
|
||||
var newBuffer = _pool.Rent(newBufferLength);
|
||||
Array.Copy(_buffer, newBuffer, _buffer.Length);
|
||||
_pool.Return(_buffer);
|
||||
_buffer = newBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,106 +14,128 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, Func<MqttClientConnectedEventArgs, Task> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.ConnectedHandler = null;
|
||||
return client;
|
||||
return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
|
||||
}
|
||||
|
||||
client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(handler);
|
||||
return client;
|
||||
return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, Action<MqttClientConnectedEventArgs> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.ConnectedHandler = null;
|
||||
return client;
|
||||
return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
|
||||
}
|
||||
|
||||
client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(handler);
|
||||
return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, IMqttClientConnectedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.ConnectedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, Func<MqttClientDisconnectedEventArgs, Task> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.DisconnectedHandler = null;
|
||||
return client;
|
||||
return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
|
||||
}
|
||||
|
||||
client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(handler);
|
||||
return client;
|
||||
return client.UseDisconnectedHandler(new MqttClientDisconnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, Action<MqttClientDisconnectedEventArgs> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.DisconnectedHandler = null;
|
||||
return client;
|
||||
return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
|
||||
}
|
||||
|
||||
client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(handler);
|
||||
return client.UseDisconnectedHandler(new MqttClientDisconnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, IMqttClientDisconnectedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.DisconnectedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, Func<MqttApplicationMessageReceivedEventArgs, Task> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.ApplicationMessageReceivedHandler = null;
|
||||
return client;
|
||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(handler);
|
||||
|
||||
return client;
|
||||
return client.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, Action<MqttApplicationMessageReceivedEventArgs> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.ApplicationMessageReceivedHandler = null;
|
||||
return client;
|
||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(handler);
|
||||
return client.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, IMqttApplicationMessageReceivedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.ApplicationMessageReceivedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IManagedMqttClient managedClient, params TopicFilter[] topicFilters)
|
||||
public static Task SubscribeAsync(this IManagedMqttClient client, params TopicFilter[] topicFilters)
|
||||
{
|
||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return managedClient.SubscribeAsync(topicFilters);
|
||||
return client.SubscribeAsync(topicFilters);
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IManagedMqttClient managedClient, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
public static Task SubscribeAsync(this IManagedMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return managedClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
||||
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IManagedMqttClient managedClient, string topic)
|
||||
public static Task SubscribeAsync(this IManagedMqttClient client, string topic)
|
||||
{
|
||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return managedClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
|
||||
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
|
||||
}
|
||||
|
||||
public static Task UnsubscribeAsync(this IManagedMqttClient managedClient, params string[] topicFilters)
|
||||
public static Task UnsubscribeAsync(this IManagedMqttClient client, params string[] topicFilters)
|
||||
{
|
||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return managedClient.UnsubscribeAsync(topicFilters);
|
||||
return client.UnsubscribeAsync(topicFilters);
|
||||
}
|
||||
|
||||
public static async Task PublishAsync(this IManagedMqttClient client, IEnumerable<MqttApplicationMessage> applicationMessages)
|
||||
@@ -190,12 +212,16 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder, CancellationToken cancellationToken)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
||||
return client.PublishAsync(message, cancellationToken);
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
||||
return client.PublishAsync(message, CancellationToken.None);
|
||||
}
|
||||
|
||||
@@ -119,11 +119,6 @@ namespace MQTTnet.Extensions.Rpc
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
if (tcs.Task.IsCompleted || tcs.Task.IsCanceled)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
tcs.TrySetResult(eventArgs.ApplicationMessage.Payload);
|
||||
|
||||
return Task.FromResult(0);
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
/// </summary>
|
||||
public RetainedApplicationMessagesModel RetainedApplicationMessages { get; set; } = new RetainedApplicationMessagesModel();
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables the MQTTnet internal logging.
|
||||
/// </summary>
|
||||
public bool EnableDebugLogging { get; set; } = false;
|
||||
}
|
||||
}
|
||||
@@ -60,13 +60,13 @@ namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
if (IPv4 == "*")
|
||||
{
|
||||
address = IPAddress.Parse("0.0.0.0");
|
||||
address = IPAddress.Any;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPv4 == "localhost")
|
||||
{
|
||||
address = IPAddress.Parse("127.0.0.1");
|
||||
address = IPAddress.Loopback;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -81,10 +81,8 @@ namespace MQTTnet.Server.Configuration
|
||||
address = ip;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.Exception($"Could not parse IPv4 address: {IPv4}");
|
||||
}
|
||||
|
||||
throw new System.Exception($"Could not parse IPv4 address: {IPv4}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -95,13 +93,13 @@ namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
if (IPv6 == "*")
|
||||
{
|
||||
address = IPAddress.Parse("::");
|
||||
address = IPAddress.IPv6Any;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPv6 == "localhost")
|
||||
{
|
||||
address = IPAddress.Parse("::1");
|
||||
address = IPAddress.IPv6Loopback;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -116,10 +114,8 @@ namespace MQTTnet.Server.Configuration
|
||||
address = ip;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.Exception($"Could not parse IPv6 address: {IPv6}");
|
||||
}
|
||||
|
||||
throw new System.Exception($"Could not parse IPv6 address: {IPv6}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MQTTnet.Server.Mqtt;
|
||||
using MQTTnet.Server.Status;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
public class ClientsController : ControllerBase
|
||||
{
|
||||
private readonly MqttServerService _mqttServerService;
|
||||
|
||||
public ClientsController(MqttServerService mqttServerService)
|
||||
{
|
||||
_mqttServerService = mqttServerService ?? throw new ArgumentNullException(nameof(mqttServerService));
|
||||
}
|
||||
|
||||
[Route("api/v1/clients")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IList<IMqttClientStatus>>> GetClients()
|
||||
{
|
||||
return new ObjectResult(await _mqttServerService.GetClientStatusAsync());
|
||||
}
|
||||
|
||||
[Route("api/v1/clients/{clientId}")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IMqttClientStatus>> GetClient(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var client = (await _mqttServerService.GetClientStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (client == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return new ObjectResult(client);
|
||||
}
|
||||
|
||||
[Route("api/v1/clients/{clientId}")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeleteClient(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var client = (await _mqttServerService.GetClientStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (client == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
await client.DisconnectAsync();
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MQTTnet.Server.Mqtt;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
public class RetainedApplicationMessagesController : ControllerBase
|
||||
{
|
||||
private readonly MqttServerService _mqttServerService;
|
||||
|
||||
public RetainedApplicationMessagesController(MqttServerService mqttServerService)
|
||||
{
|
||||
_mqttServerService = mqttServerService ?? throw new ArgumentNullException(nameof(mqttServerService));
|
||||
}
|
||||
|
||||
[Route("api/v1/retainedApplicationMessages")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IList<MqttApplicationMessage>>> GetRetainedApplicationMessages()
|
||||
{
|
||||
return new ObjectResult(await _mqttServerService.GetRetainedApplicationMessagesAsync());
|
||||
}
|
||||
|
||||
[Route("api/v1/retainedApplicationMessages/{topic}")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<MqttApplicationMessage>> GetRetainedApplicationMessage(string topic)
|
||||
{
|
||||
topic = HttpUtility.UrlDecode(topic);
|
||||
|
||||
var applicationMessage = (await _mqttServerService.GetRetainedApplicationMessagesAsync()).FirstOrDefault(c => c.Topic == topic);
|
||||
if (applicationMessage == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return new ObjectResult(applicationMessage);
|
||||
}
|
||||
|
||||
[Route("api/v1/retainedApplicationMessages")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeleteRetainedApplicationMessages()
|
||||
{
|
||||
await _mqttServerService.ClearRetainedApplicationMessagesAsync();
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
|
||||
[Route("api/v1/retainedApplicationMessages/{topic}")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeleteRetainedApplicationMessage(string topic)
|
||||
{
|
||||
topic = HttpUtility.UrlDecode(topic);
|
||||
|
||||
await _mqttServerService.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).WithRetainFlag().Build());
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
public class ScriptsController
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MQTTnet.Server.Mqtt;
|
||||
using MQTTnet.Server.Status;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
public class SessionsController : ControllerBase
|
||||
{
|
||||
private readonly MqttServerService _mqttServerService;
|
||||
|
||||
public SessionsController(MqttServerService mqttServerService)
|
||||
{
|
||||
_mqttServerService = mqttServerService ?? throw new ArgumentNullException(nameof(mqttServerService));
|
||||
}
|
||||
|
||||
[Route("api/v1/sessions")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IList<IMqttSessionStatus>>> GetSessions()
|
||||
{
|
||||
return new ObjectResult(await _mqttServerService.GetSessionStatusAsync());
|
||||
}
|
||||
|
||||
[Route("api/v1/sessions/{clientId}")]
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IMqttClientStatus>> GetSession(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var session = (await _mqttServerService.GetSessionStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (session == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return new ObjectResult(session);
|
||||
}
|
||||
|
||||
[Route("api/v1/sessions/{clientId}")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeleteSession(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var session = (await _mqttServerService.GetSessionStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (session == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
await session.DeleteAsync();
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
|
||||
[Route("api/v1/sessions/{clientId}/pendingApplicationMessages")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeletePendingApplicationMessages(string clientId)
|
||||
{
|
||||
clientId = HttpUtility.UrlDecode(clientId);
|
||||
|
||||
var session = (await _mqttServerService.GetSessionStatusAsync()).FirstOrDefault(c => c.ClientId == clientId);
|
||||
if (session == null)
|
||||
{
|
||||
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
await session.ClearPendingApplicationMessagesAsync();
|
||||
return StatusCode((int)HttpStatusCode.NoContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MQTTnet.Server.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class ValuesController : ControllerBase
|
||||
{
|
||||
// GET api/values
|
||||
[HttpGet]
|
||||
public ActionResult<IEnumerable<string>> Get()
|
||||
{
|
||||
return new string[] { "value1", "value2" };
|
||||
}
|
||||
|
||||
// GET api/values/5
|
||||
[HttpGet("{id}")]
|
||||
public ActionResult<string> Get(int id)
|
||||
{
|
||||
return "value";
|
||||
}
|
||||
|
||||
// POST api/values
|
||||
[HttpPost]
|
||||
public void Post([FromBody] string value)
|
||||
{
|
||||
}
|
||||
|
||||
// PUT api/values/5
|
||||
[HttpPut("{id}")]
|
||||
public void Put(int id, [FromBody] string value)
|
||||
{
|
||||
}
|
||||
|
||||
// DELETE api/values/5
|
||||
[HttpDelete("{id}")]
|
||||
public void Delete(int id)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
@@ -14,7 +15,7 @@ namespace MQTTnet.Server.Logging
|
||||
}
|
||||
|
||||
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
||||
|
||||
|
||||
public IMqttNetChildLogger CreateChildLogger(string source = null)
|
||||
{
|
||||
return new MqttNetChildLoggerWrapper(source, this);
|
||||
@@ -24,8 +25,15 @@ namespace MQTTnet.Server.Logging
|
||||
{
|
||||
var convertedLogLevel = ConvertLogLevel(logLevel);
|
||||
_logger.Log(convertedLogLevel, exception, message, parameters);
|
||||
}
|
||||
|
||||
var logMessagePublishedEvent = LogMessagePublished;
|
||||
if (logMessagePublishedEvent != null)
|
||||
{
|
||||
var logMessage = new MqttNetLogMessage(null, DateTime.UtcNow, Thread.CurrentThread.ManagedThreadId, source, logLevel, message, exception);
|
||||
logMessagePublishedEvent.Invoke(this, new MqttNetLogMessagePublishedEventArgs(logMessage));
|
||||
}
|
||||
}
|
||||
|
||||
private static LogLevel ConvertLogLevel(MqttNetLogLevel logLevel)
|
||||
{
|
||||
switch (logLevel)
|
||||
|
||||
@@ -27,23 +27,10 @@
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="wwwroot\app\app.ts" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\app\app.js">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\app\app.js.map">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\Index.html">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -53,8 +40,8 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="4.0.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="4.0.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.0.0-rc2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.0.0-rc2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -81,9 +68,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<TypeScriptCompile Include="wwwroot\app\app.ts">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</TypeScriptCompile>
|
||||
<Folder Include="wwwroot\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -46,21 +46,5 @@ namespace MQTTnet.Server.Mqtt
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// TODO: Create dump(object) method in wrapper (creates JSON and prints it).
|
||||
public class PythonMqttApplicationMessageInterceptorContext
|
||||
{
|
||||
public bool accept_connection;
|
||||
|
||||
public bool accept_publish;
|
||||
|
||||
public string client_id;
|
||||
|
||||
public string topic;
|
||||
|
||||
public int qos;
|
||||
|
||||
public bool retain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,12 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server.Configuration;
|
||||
using MQTTnet.Server.Scripting;
|
||||
using MQTTnet.Server.Status;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
@@ -92,6 +94,33 @@ namespace MQTTnet.Server.Mqtt
|
||||
return _webSocketServerAdapter.RunWebSocketConnectionAsync(webSocket, httpContext);
|
||||
}
|
||||
|
||||
public Task<IList<IMqttClientStatus>> GetClientStatusAsync()
|
||||
{
|
||||
return _mqttServer.GetClientStatusAsync();
|
||||
}
|
||||
|
||||
public Task<IList<IMqttSessionStatus>> GetSessionStatusAsync()
|
||||
{
|
||||
return _mqttServer.GetSessionStatusAsync();
|
||||
}
|
||||
|
||||
public Task ClearRetainedApplicationMessagesAsync()
|
||||
{
|
||||
return _mqttServer.ClearRetainedApplicationMessagesAsync();
|
||||
}
|
||||
|
||||
public Task<IList<MqttApplicationMessage>> GetRetainedApplicationMessagesAsync()
|
||||
{
|
||||
return _mqttServer.GetRetainedApplicationMessagesAsync();
|
||||
}
|
||||
|
||||
public Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
return _mqttServer.PublishAsync(applicationMessage);
|
||||
}
|
||||
|
||||
private void Publish(PythonDictionary parameters)
|
||||
{
|
||||
try
|
||||
@@ -152,6 +181,7 @@ namespace MQTTnet.Server.Mqtt
|
||||
if (_settings.TcpEndPoint.Enabled)
|
||||
{
|
||||
options.WithDefaultEndpoint();
|
||||
|
||||
if (_settings.TcpEndPoint.TryReadIPv4(out var address4))
|
||||
{
|
||||
options.WithDefaultEndpointBoundIPAddress(address4);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
@@ -12,7 +13,8 @@ namespace MQTTnet.Server
|
||||
{
|
||||
PrintLogo();
|
||||
|
||||
CreateWebHostBuilder(args).Build().Run();
|
||||
WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build().Run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -22,23 +24,25 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
private static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseKestrel()
|
||||
.UseStartup<Startup>();
|
||||
|
||||
private static void PrintLogo()
|
||||
{
|
||||
Console.ResetColor();
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(@"
|
||||
__ __ ____ _______ _______ _ _____
|
||||
| \/ |/ __ \__ __|__ __| | | / ____|
|
||||
| \ / | | | | | | | |_ __ ___| |_ | (___ ___ _ ____ _____ _ __
|
||||
| |\/| | | | | | | | | '_ \ / _ \ __| \___ \ / _ \ '__\ \ / / _ \ '__|
|
||||
| | | | |__| | | | | | | | | __/ |_ ____) | __/ | \ V / __/ |
|
||||
|_| |_|\___\_\ |_| |_|_| |_|\___|\__| |_____/ \___|_| \_/ \___|_|");
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.BackgroundColor = ConsoleColor.Red;
|
||||
const string LogoText =
|
||||
@"| |
|
||||
| __ __ ____ _______ _______ _ _____ |
|
||||
| | \/ |/ __ \__ __|__ __| | | / ____| |
|
||||
| | \ / | | | | | | | |_ __ ___| |_ | (___ ___ _ ____ _____ _ __ |
|
||||
| | |\/| | | | | | | | | '_ \ / _ \ __| \___ \ / _ \ '__\ \ / / _ \ '__| |
|
||||
| | | | | |__| | | | | | | | | __/ |_ ____) | __/ | \ V / __/ | |
|
||||
| |_| |_|\___\_\ |_| |_|_| |_|\___|\__| |_____/ \___|_| \_/ \___|_| |
|
||||
| |
|
||||
| |";
|
||||
|
||||
Console.WriteLine(LogoText);
|
||||
Console.ResetColor();
|
||||
Console.WriteLine();
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine(@"
|
||||
-- The official MQTT server implementation of MQTTnet --
|
||||
@@ -49,8 +53,8 @@ namespace MQTTnet.Server
|
||||
https://github.com/chkr1011/MQTTnet");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine(@"
|
||||
Version: 1.0.0-alpha1
|
||||
Console.WriteLine($@"
|
||||
Version: {Assembly.GetExecutingAssembly().GetName().Version}
|
||||
License: MIT (read LICENSE file)
|
||||
Sponsoring: https://opencollective.com/mqttnet
|
||||
Support: https://github.com/chkr1011/MQTTnet/issues
|
||||
|
||||
@@ -5,12 +5,14 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.Scripting.Utils;
|
||||
using MQTTnet.Server.Configuration;
|
||||
using MQTTnet.Server.Logging;
|
||||
using MQTTnet.Server.Mqtt;
|
||||
using MQTTnet.Server.Scripting;
|
||||
using MQTTnet.Server.Scripting.DataSharing;
|
||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
@@ -55,11 +57,28 @@ namespace MQTTnet.Server
|
||||
pythonScriptHostService.Configure();
|
||||
|
||||
mqttServerService.Configure();
|
||||
|
||||
application.UseSwagger(o => o.RouteTemplate = "/api/{documentName}/swagger.json");
|
||||
|
||||
application.UseSwaggerUI(o =>
|
||||
{
|
||||
o.RoutePrefix = "api";
|
||||
o.DocumentTitle = "MQTTnet.Server API";
|
||||
o.SwaggerEndpoint("/api/v1/swagger.json", "MQTTnet.Server API v1");
|
||||
o.DisplayRequestDuration();
|
||||
o.DocExpansion(DocExpansion.List);
|
||||
o.DefaultModelRendering(ModelRendering.Model);
|
||||
});
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
|
||||
services.AddMvc()
|
||||
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
|
||||
.AddJsonOptions(options =>
|
||||
{
|
||||
options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
|
||||
});
|
||||
|
||||
services.AddSingleton(ReadSettings());
|
||||
|
||||
@@ -79,6 +98,28 @@ namespace MQTTnet.Server
|
||||
services.AddSingleton<MqttConnectionValidator>();
|
||||
services.AddSingleton<MqttSubscriptionInterceptor>();
|
||||
services.AddSingleton<MqttApplicationMessageInterceptor>();
|
||||
|
||||
services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.DescribeAllEnumsAsStrings();
|
||||
c.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Title = "MQTTnet.Server API",
|
||||
Version = "v1",
|
||||
Description = "The public API for the MQTT broker MQTTnet.Server.",
|
||||
License = new OpenApiLicense
|
||||
{
|
||||
Name = "MIT",
|
||||
Url = new Uri("https://github.com/chkr1011/MQTTnet/blob/master/README.md")
|
||||
},
|
||||
Contact = new OpenApiContact
|
||||
{
|
||||
Name = "MQTTnet.Server",
|
||||
Email = string.Empty,
|
||||
Url = new Uri("https://github.com/chkr1011/MQTTnet")
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private SettingsModel ReadSettings()
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
*/
|
||||
"TcpEndPoint": {
|
||||
"Enabled": true,
|
||||
"IPv4": "localhost",
|
||||
"IPv6": "localhost",
|
||||
"IPv4": "*",
|
||||
"IPv6": "*",
|
||||
"Port": 1883
|
||||
},
|
||||
"EncryptedTcpEndPoint": {
|
||||
"Enabled": false,
|
||||
"IPv4": "localhost",
|
||||
"IPv6": "localhost",
|
||||
"IPv4": "*",
|
||||
"IPv6": "*",
|
||||
"Port": 8883,
|
||||
"CertificatePath": "/absolute/path/to/pfx"
|
||||
},
|
||||
@@ -45,7 +45,7 @@
|
||||
"Filename": "RetainedApplicationMessages.json",
|
||||
"WriteInterval": 10 // In seconds.
|
||||
},
|
||||
"EnableDebugLogging": false
|
||||
"EnableDebugLogging": true
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": false,
|
||||
"noEmitOnError": true,
|
||||
"removeComments": false,
|
||||
"sourceMap": true,
|
||||
"target": "es5"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>MQTTnet Server test</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="node_modules/systemjs/dist/system.js"></script>
|
||||
<script>
|
||||
System.config({
|
||||
packages: {
|
||||
'': {
|
||||
defaultExtension: 'js'
|
||||
}
|
||||
},
|
||||
map: {
|
||||
mqtt: 'node_modules/mqtt/dist/mqtt.min'
|
||||
}
|
||||
});
|
||||
System.import('app/app');
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<p id="state"></p>
|
||||
<input id="topic" placeholder="topic" />
|
||||
<input id="msg" placeholder="msg" />
|
||||
<button id="publish">send</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ul id="msgs" ></ul>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,58 +0,0 @@
|
||||
import { connect } from "mqtt";
|
||||
|
||||
var client = connect('ws://' + location.host + '/mqtt',
|
||||
{
|
||||
clientId: "client" + Math.floor(Math.random() * 6) + 1
|
||||
});
|
||||
|
||||
window.onbeforeunload = () => {
|
||||
client.end();
|
||||
};
|
||||
|
||||
var publishButton = document.getElementById("publish");
|
||||
var topicInput = <HTMLInputElement>document.getElementById("topic");
|
||||
var msgInput = <HTMLInputElement>document.getElementById("msg");
|
||||
var stateParagraph = document.getElementById("state");
|
||||
var msgsList = <HTMLUListElement>document.getElementById("msgs");
|
||||
|
||||
publishButton.onclick = click => {
|
||||
var topic = topicInput.value;
|
||||
var msg = msgInput.value;
|
||||
client.publish(topic, msg);
|
||||
};
|
||||
|
||||
client.on('connect', () => {
|
||||
client.subscribe('#', { qos: 0 }, (err, granted) => {
|
||||
console.log(err);
|
||||
});
|
||||
client.publish('presence', 'Hello mqtt');
|
||||
|
||||
stateParagraph.innerText = "connected";
|
||||
showMsg("[connect]");
|
||||
});
|
||||
|
||||
client.on("error", e => {
|
||||
showMsg("error: " + e.message);
|
||||
});
|
||||
|
||||
client.on("reconnect", () => {
|
||||
stateParagraph.innerText = "reconnecting";
|
||||
showMsg("[reconnect]");
|
||||
});
|
||||
|
||||
client.on('message', (topic, message) => {
|
||||
showMsg(topic + ": " + message.toString());
|
||||
});
|
||||
|
||||
function showMsg(msg: string) {
|
||||
//console.log(msg);
|
||||
|
||||
var node = document.createElement("LI");
|
||||
node.appendChild(document.createTextNode(msg));
|
||||
|
||||
msgsList.appendChild(node);
|
||||
|
||||
if (msgsList.childElementCount > 50) {
|
||||
msgsList.removeChild(msgsList.childNodes[0]);
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,14 @@ namespace MQTTnet.Adapter
|
||||
bool IsSecureConnection { get; }
|
||||
|
||||
MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
event EventHandler ReadingPacketStarted;
|
||||
|
||||
event EventHandler ReadingPacketCompleted;
|
||||
long BytesSent { get; }
|
||||
|
||||
long BytesReceived { get; }
|
||||
|
||||
Action ReadingPacketStartedCallback { get; set; }
|
||||
|
||||
Action ReadingPacketCompletedCallback { get; set; }
|
||||
|
||||
Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
public interface IMqttServerAdapter : IDisposable
|
||||
{
|
||||
Action<MqttServerAdapterClientAcceptedEventArgs> ClientAcceptedHandler { get; set; }
|
||||
Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
Task StartAsync(IMqttServerOptions options);
|
||||
Task StopAsync();
|
||||
|
||||
@@ -25,16 +25,19 @@ namespace MQTTnet.Adapter
|
||||
private readonly MqttPacketReader _packetReader;
|
||||
|
||||
private readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
private long _bytesReceived;
|
||||
private long _bytesSent;
|
||||
|
||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetChildLogger logger)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
|
||||
|
||||
_packetReader = new MqttPacketReader(_channel);
|
||||
|
||||
_logger = logger.CreateChildLogger(nameof(MqttChannelAdapter));
|
||||
@@ -46,9 +49,12 @@ namespace MQTTnet.Adapter
|
||||
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
public event EventHandler ReadingPacketStarted;
|
||||
public event EventHandler ReadingPacketCompleted;
|
||||
public long BytesSent => Interlocked.Read(ref _bytesSent);
|
||||
public long BytesReceived => Interlocked.Read(ref _bytesReceived);
|
||||
|
||||
public Action ReadingPacketStartedCallback { get; set; }
|
||||
public Action ReadingPacketCompletedCallback { get; set; }
|
||||
|
||||
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
@@ -62,7 +68,7 @@ namespace MQTTnet.Adapter
|
||||
else
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(t => _channel.ConnectAsync(t), timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -101,7 +107,7 @@ namespace MQTTnet.Adapter
|
||||
WrapException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
@@ -118,7 +124,9 @@ namespace MQTTnet.Adapter
|
||||
await MqttTaskTimeout.WaitAsync(
|
||||
t => _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, t), timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
Interlocked.Add(ref _bytesReceived, packetData.Count);
|
||||
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
|
||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
|
||||
@@ -159,7 +167,9 @@ namespace MQTTnet.Adapter
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!PacketFormatterAdapter.ProtocolVersion.HasValue)
|
||||
Interlocked.Add(ref _bytesSent, receivedMqttPacket.TotalLength);
|
||||
|
||||
if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket);
|
||||
}
|
||||
@@ -190,10 +200,21 @@ namespace MQTTnet.Adapter
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ResetStatistics()
|
||||
{
|
||||
Interlocked.Exchange(ref _bytesReceived, 0L);
|
||||
Interlocked.Exchange(ref _bytesSent, 0L);
|
||||
}
|
||||
|
||||
private async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var readFixedHeaderResult = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (readFixedHeaderResult.ConnectionClosed)
|
||||
@@ -201,7 +222,7 @@ namespace MQTTnet.Adapter
|
||||
return null;
|
||||
}
|
||||
|
||||
ReadingPacketStarted?.Invoke(this, EventArgs.Empty);
|
||||
ReadingPacketStartedCallback?.Invoke();
|
||||
|
||||
var fixedHeader = readFixedHeaderResult.FixedHeader;
|
||||
if (fixedHeader.RemainingLength == 0)
|
||||
@@ -221,13 +242,7 @@ namespace MQTTnet.Adapter
|
||||
chunkSize = bytesLeft;
|
||||
}
|
||||
|
||||
#if WINDOWS_UWP
|
||||
var readBytes = await _channel.ReadAsync(body, bodyOffset, (int)chunkSize, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
// async/await is not used to avoid the overhead of context switches. We assume that the remaining data
|
||||
// has been sent from the sender directly after the initial bytes.
|
||||
var readBytes = _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
var readBytes = await _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -238,7 +253,7 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
bodyOffset += readBytes;
|
||||
} while (bodyOffset < body.Length);
|
||||
|
||||
@@ -247,7 +262,7 @@ namespace MQTTnet.Adapter
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReadingPacketCompleted?.Invoke(this, EventArgs.Empty);
|
||||
ReadingPacketCompletedCallback?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,15 +283,19 @@ namespace MQTTnet.Adapter
|
||||
|
||||
private static bool IsWrappedException(Exception exception)
|
||||
{
|
||||
return exception is TaskCanceledException ||
|
||||
exception is OperationCanceledException ||
|
||||
return exception is OperationCanceledException ||
|
||||
exception is MqttCommunicationTimedOutException ||
|
||||
exception is MqttCommunicationException;
|
||||
}
|
||||
|
||||
private static void WrapException(Exception exception)
|
||||
{
|
||||
if (exception is IOException && exception.InnerException is SocketException socketException)
|
||||
if (exception is IOException && exception.InnerException is SocketException innerException)
|
||||
{
|
||||
exception = innerException;
|
||||
}
|
||||
|
||||
if (exception is SocketException socketException)
|
||||
{
|
||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted ||
|
||||
socketException.SocketErrorCode == SocketError.OperationAborted)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public class MqttServerAdapterClientAcceptedEventArgs : EventArgs
|
||||
{
|
||||
public MqttServerAdapterClientAcceptedEventArgs(IMqttChannelAdapter client)
|
||||
{
|
||||
Client = client ?? throw new ArgumentNullException(nameof(client));
|
||||
}
|
||||
|
||||
public IMqttChannelAdapter Client { get; }
|
||||
|
||||
public Task SessionTask { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,16 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
public class ReceivedMqttPacket
|
||||
{
|
||||
public ReceivedMqttPacket(byte fixedHeader, MqttPacketBodyReader body, int totalLength)
|
||||
public ReceivedMqttPacket(byte fixedHeader, IMqttPacketBodyReader body, int totalLength)
|
||||
{
|
||||
FixedHeader = fixedHeader;
|
||||
Body = body;
|
||||
TotalLength = totalLength;
|
||||
}
|
||||
|
||||
public byte FixedHeader { get; }
|
||||
public byte FixedHeader { get; set; }
|
||||
|
||||
public MqttPacketBodyReader Body { get; }
|
||||
public IMqttPacketBodyReader Body { get; }
|
||||
|
||||
public int TotalLength { get; }
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
catch (MqttCommunicationTimedOutException)
|
||||
{
|
||||
_logger.Warning(null, "Timeout while waiting for packet of type '{0}'.", typeof(TResponsePacket).Namespace);
|
||||
_logger.Warning(null, "Timeout while waiting for packet of type '{0}'.", typeof(TResponsePacket).Name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,82 +17,97 @@ namespace MQTTnet.Client
|
||||
{
|
||||
public static IMqttClient UseConnectedHandler(this IMqttClient client, Func<MqttClientConnectedEventArgs, Task> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.ConnectedHandler = null;
|
||||
return client;
|
||||
return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
|
||||
}
|
||||
|
||||
client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(handler);
|
||||
return client;
|
||||
return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttClient UseConnectedHandler(this IMqttClient client, Action<MqttClientConnectedEventArgs> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.ConnectedHandler = null;
|
||||
return client;
|
||||
return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
|
||||
}
|
||||
|
||||
client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(handler);
|
||||
return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttClient UseConnectedHandler(this IMqttClient client, IMqttClientConnectedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.ConnectedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static IMqttClient UseDisconnectedHandler(this IMqttClient client, Func<MqttClientDisconnectedEventArgs, Task> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.DisconnectedHandler = null;
|
||||
return client;
|
||||
return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
|
||||
}
|
||||
|
||||
client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(handler);
|
||||
return client;
|
||||
return client.UseDisconnectedHandler(new MqttClientDisconnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttClient UseDisconnectedHandler(this IMqttClient client, Action<MqttClientDisconnectedEventArgs> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.DisconnectedHandler = null;
|
||||
return client;
|
||||
return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
|
||||
}
|
||||
|
||||
client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(handler);
|
||||
return client.UseDisconnectedHandler(new MqttClientDisconnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttClient UseDisconnectedHandler(this IMqttClient client, IMqttClientDisconnectedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.DisconnectedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static IMqttClient UseApplicationMessageReceivedHandler(this IMqttClient client, Func<MqttApplicationMessageReceivedEventArgs, Task> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.ApplicationMessageReceivedHandler = null;
|
||||
return client;
|
||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(handler);
|
||||
|
||||
return client;
|
||||
return client.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttClient UseApplicationMessageReceivedHandler(this IMqttClient client, Action<MqttApplicationMessageReceivedEventArgs> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
client.ApplicationMessageReceivedHandler = null;
|
||||
return client;
|
||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(handler);
|
||||
|
||||
return client;
|
||||
return client.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttClient UseApplicationMessageReceivedHandler(this IMqttClient client, IMqttApplicationMessageReceivedHandler handler)
|
||||
{
|
||||
client.ApplicationMessageReceivedHandler = handler;
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.ApplicationMessageReceivedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,11 @@ namespace MQTTnet.Client.Options
|
||||
|
||||
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
|
||||
{
|
||||
if (value == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
throw new ArgumentException("Protocol version is invalid.");
|
||||
}
|
||||
|
||||
_options.ProtocolVersion = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace MQTTnet.Client.Options
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientTcpOptions : IMqttClientChannelOptions
|
||||
{
|
||||
@@ -6,7 +8,13 @@
|
||||
|
||||
public int? Port { get; set; }
|
||||
|
||||
public int BufferSize { get; set; } = 4096;
|
||||
public int BufferSize { get; set; } = 65536;
|
||||
|
||||
public bool DualMode { get; set; } = true;
|
||||
|
||||
public bool NoDelay { get; set; } = true;
|
||||
|
||||
public AddressFamily AddressFamily { get; set; } = AddressFamily.Unspecified;
|
||||
|
||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
||||
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client.Publishing
|
||||
{
|
||||
public class MqttClientPublishResult
|
||||
{
|
||||
public ushort? PacketIdentifier { get; set; }
|
||||
|
||||
public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success;
|
||||
|
||||
public string ReasonString { get; set; }
|
||||
|
||||
public List<MqttUserProperty> UserProperties { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace MQTTnet.Formatter
|
||||
{
|
||||
MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage);
|
||||
|
||||
MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket);
|
||||
|
||||
MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket);
|
||||
|
||||
MqttClientAuthenticateResult CreateClientConnectResult(MqttConnAckPacket connAckPacket);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public interface IMqttPacketBodyReader
|
||||
{
|
||||
ulong Length { get; }
|
||||
|
||||
ulong Offset { get; }
|
||||
|
||||
bool EndOfStream { get; }
|
||||
|
||||
byte ReadByte();
|
||||
|
||||
byte[] ReadRemainingData();
|
||||
|
||||
ushort ReadTwoByteInteger();
|
||||
|
||||
string ReadStringWithLengthPrefix();
|
||||
|
||||
byte[] ReadWithLengthPrefix();
|
||||
|
||||
uint ReadFourByteInteger();
|
||||
|
||||
uint ReadVariableLengthInteger();
|
||||
|
||||
bool ReadBoolean();
|
||||
|
||||
void Seek(ulong position);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public interface IMqttPacketWriter
|
||||
{
|
||||
int Length { get; }
|
||||
|
||||
void WriteWithLengthPrefix(string value);
|
||||
void Write(byte returnCode);
|
||||
void WriteWithLengthPrefix(byte[] payload);
|
||||
void Write(ushort keepAlivePeriod);
|
||||
|
||||
void Write(IMqttPacketWriter propertyWriter);
|
||||
void WriteVariableLengthInteger(uint length);
|
||||
void Write(byte[] payload, int v, int length);
|
||||
void Reset(int v);
|
||||
void Seek(int v);
|
||||
void FreeBuffer();
|
||||
byte[] GetBuffer();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public class MqttPacketBodyReader
|
||||
public class MqttPacketBodyReader : IMqttPacketBodyReader
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
private readonly ulong _initialOffset;
|
||||
@@ -68,9 +69,9 @@ namespace MQTTnet.Formatter
|
||||
throw new MqttProtocolViolationException("Boolean values can be 0 or 1 only.");
|
||||
}
|
||||
|
||||
public ArraySegment<byte> ReadRemainingData()
|
||||
public byte[] ReadRemainingData()
|
||||
{
|
||||
return new ArraySegment<byte>(_buffer, (int)Offset, (int)(_length - Offset));
|
||||
return new ArraySegment<byte>(_buffer, (int)Offset, (int)(_length - Offset)).ToArray();
|
||||
}
|
||||
|
||||
public ushort ReadTwoByteInteger()
|
||||
@@ -95,7 +96,12 @@ namespace MQTTnet.Formatter
|
||||
return (uint)(byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3);
|
||||
}
|
||||
|
||||
public ArraySegment<byte> ReadWithLengthPrefix()
|
||||
public byte[] ReadWithLengthPrefix()
|
||||
{
|
||||
return ReadSegmentWithLengthPrefix().ToArray();
|
||||
}
|
||||
|
||||
private ArraySegment<byte> ReadSegmentWithLengthPrefix()
|
||||
{
|
||||
var length = ReadTwoByteInteger();
|
||||
|
||||
@@ -140,7 +146,7 @@ namespace MQTTnet.Formatter
|
||||
|
||||
public string ReadStringWithLengthPrefix()
|
||||
{
|
||||
var buffer = ReadWithLengthPrefix();
|
||||
var buffer = ReadSegmentWithLengthPrefix();
|
||||
return Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,27 +12,44 @@ namespace MQTTnet.Formatter
|
||||
private IMqttPacketFormatter _formatter;
|
||||
|
||||
public MqttPacketFormatterAdapter()
|
||||
: this(new MqttPacketWriter())
|
||||
{
|
||||
}
|
||||
|
||||
public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion)
|
||||
: this(protocolVersion, new MqttPacketWriter())
|
||||
{
|
||||
}
|
||||
|
||||
public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion)
|
||||
public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion, IMqttPacketWriter writer)
|
||||
: this(writer)
|
||||
{
|
||||
UseProtocolVersion(protocolVersion);
|
||||
}
|
||||
|
||||
public MqttProtocolVersion? ProtocolVersion { get; private set; }
|
||||
public MqttPacketFormatterAdapter(IMqttPacketWriter writer)
|
||||
{
|
||||
Writer = writer;
|
||||
}
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown;
|
||||
|
||||
public IMqttDataConverter DataConverter
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfFormatterNotSet();
|
||||
|
||||
return _formatter.DataConverter;
|
||||
}
|
||||
}
|
||||
|
||||
public IMqttPacketWriter Writer { get; }
|
||||
|
||||
public ArraySegment<byte> Encode(MqttBasePacket packet)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
|
||||
ThrowIfFormatterNotSet();
|
||||
|
||||
return _formatter.Encode(packet);
|
||||
@@ -40,6 +57,8 @@ namespace MQTTnet.Formatter
|
||||
|
||||
public MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
||||
|
||||
ThrowIfFormatterNotSet();
|
||||
|
||||
return _formatter.Decode(receivedMqttPacket);
|
||||
@@ -64,29 +83,36 @@ namespace MQTTnet.Formatter
|
||||
|
||||
private void UseProtocolVersion(MqttProtocolVersion protocolVersion)
|
||||
{
|
||||
ProtocolVersion = protocolVersion;
|
||||
if (protocolVersion == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
throw new InvalidOperationException("MQTT protocol version is invalid.");
|
||||
}
|
||||
|
||||
ProtocolVersion = protocolVersion;
|
||||
_formatter = GetMqttPacketFormatter(protocolVersion, Writer);
|
||||
}
|
||||
|
||||
public static IMqttPacketFormatter GetMqttPacketFormatter(MqttProtocolVersion protocolVersion, IMqttPacketWriter writer)
|
||||
{
|
||||
if (protocolVersion == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
throw new InvalidOperationException("MQTT protocol version is invalid.");
|
||||
}
|
||||
|
||||
switch (protocolVersion)
|
||||
{
|
||||
case MqttProtocolVersion.V500:
|
||||
{
|
||||
_formatter = new MqttV500PacketFormatter();
|
||||
break;
|
||||
return new MqttV500PacketFormatter(writer);
|
||||
}
|
||||
|
||||
case MqttProtocolVersion.V311:
|
||||
{
|
||||
_formatter = new MqttV311PacketFormatter();
|
||||
break;
|
||||
return new MqttV311PacketFormatter(writer);
|
||||
}
|
||||
|
||||
case MqttProtocolVersion.V310:
|
||||
{
|
||||
|
||||
_formatter = new MqttV310PacketFormatter();
|
||||
break;
|
||||
return new MqttV310PacketFormatter(writer);
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
@@ -31,8 +30,11 @@ namespace MQTTnet.Formatter
|
||||
{
|
||||
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
return new ReadFixedHeaderResult
|
||||
@@ -100,13 +102,19 @@ namespace MQTTnet.Formatter
|
||||
return null;
|
||||
}
|
||||
|
||||
var buffer = ReadByte(cancellationToken);
|
||||
if (!buffer.HasValue)
|
||||
var readCount = _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).GetAwaiter().GetResult();
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
encodedByte = buffer.Value;
|
||||
if (readCount == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
encodedByte = _singleByteBuffer[0];
|
||||
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
@@ -114,26 +122,8 @@ namespace MQTTnet.Formatter
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private byte? ReadByte(CancellationToken cancellationToken)
|
||||
{
|
||||
var readCount = _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (readCount == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _singleByteBuffer[0];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
private async Task<int?> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
|
||||
{
|
||||
var offset = 0;
|
||||
@@ -154,13 +144,19 @@ namespace MQTTnet.Formatter
|
||||
return null;
|
||||
}
|
||||
|
||||
var buffer = await ReadByteAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (!buffer.HasValue)
|
||||
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
encodedByte = buffer.Value;
|
||||
if (readCount == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
encodedByte = _singleByteBuffer[0];
|
||||
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
@@ -168,24 +164,6 @@ namespace MQTTnet.Formatter
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private async Task<byte?> ReadByteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (readCount == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _singleByteBuffer[0];
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,15 @@ namespace MQTTnet.Formatter
|
||||
/// same as for the original MemoryStream in .net. Also this implementation allows accessing the internal
|
||||
/// buffer for all platforms and .net framework versions (which is not available at the regular MemoryStream).
|
||||
/// </summary>
|
||||
public class MqttPacketWriter
|
||||
public class MqttPacketWriter : IMqttPacketWriter
|
||||
{
|
||||
private static readonly ArraySegment<byte> ZeroVariableLengthIntegerArray = new ArraySegment<byte>(new byte[1], 0, 1);
|
||||
private static readonly ArraySegment<byte> ZeroTwoByteIntegerArray = new ArraySegment<byte>(new byte[2], 0, 2);
|
||||
|
||||
public static int InitialBufferSize = 128;
|
||||
public static int MaxBufferSize = 4096;
|
||||
|
||||
// TODO: Consider using the ArrayPool here together with FreeBuffer.
|
||||
private byte[] _buffer = new byte[128];
|
||||
private byte[] _buffer = new byte[InitialBufferSize];
|
||||
|
||||
private int _offset;
|
||||
|
||||
@@ -29,11 +32,24 @@ namespace MQTTnet.Formatter
|
||||
return (byte)fixedHeader;
|
||||
}
|
||||
|
||||
public static int GetLengthOfVariableInteger(uint value)
|
||||
{
|
||||
var result = 0;
|
||||
var x = value;
|
||||
do
|
||||
{
|
||||
x = x / 128;
|
||||
result++;
|
||||
} while (x > 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArraySegment<byte> EncodeVariableLengthInteger(uint value)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
return new ArraySegment<byte>(new byte[1], 0, 1);
|
||||
return ZeroVariableLengthIntegerArray;
|
||||
}
|
||||
|
||||
if (value <= 127)
|
||||
@@ -68,17 +84,28 @@ namespace MQTTnet.Formatter
|
||||
|
||||
public void WriteWithLengthPrefix(string value)
|
||||
{
|
||||
WriteWithLengthPrefix(Encoding.UTF8.GetBytes(value ?? string.Empty));
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
Write(ZeroTwoByteIntegerArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteWithLengthPrefix(Encoding.UTF8.GetBytes(value));
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteWithLengthPrefix(byte[] value)
|
||||
{
|
||||
if (value == null) throw new ArgumentNullException(nameof(value));
|
||||
|
||||
EnsureAdditionalCapacity(value.Length + 2);
|
||||
|
||||
Write((ushort)value.Length);
|
||||
Write(value, 0, value.Length);
|
||||
if (value == null || value.Length == 0)
|
||||
{
|
||||
Write(ZeroTwoByteIntegerArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureAdditionalCapacity(value.Length + 2);
|
||||
Write((ushort)value.Length);
|
||||
Write(value, 0, value.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(byte @byte)
|
||||
@@ -103,29 +130,33 @@ namespace MQTTnet.Formatter
|
||||
{
|
||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureAdditionalCapacity(count);
|
||||
|
||||
Array.Copy(buffer, offset, _buffer, _offset, count);
|
||||
IncreasePosition(count);
|
||||
}
|
||||
|
||||
public void Write(ArraySegment<byte> buffer)
|
||||
{
|
||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
Write(buffer.Array, buffer.Offset, buffer.Count);
|
||||
}
|
||||
|
||||
public void Write(MqttPacketWriter propertyWriter)
|
||||
public void Write(IMqttPacketWriter propertyWriter)
|
||||
{
|
||||
if (propertyWriter == null) throw new ArgumentNullException(nameof(propertyWriter));
|
||||
|
||||
if (propertyWriter.Length == 0)
|
||||
if (propertyWriter is MqttPacketWriter writer)
|
||||
{
|
||||
if (writer.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Write(writer._buffer, 0, writer.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
Write(propertyWriter._buffer, 0, propertyWriter.Length);
|
||||
throw new InvalidOperationException($"{nameof(propertyWriter)} must be of type {typeof(MqttPacketWriter).Name}");
|
||||
}
|
||||
|
||||
public void Reset(int length)
|
||||
@@ -160,6 +191,11 @@ namespace MQTTnet.Formatter
|
||||
Array.Resize(ref _buffer, MaxBufferSize);
|
||||
}
|
||||
|
||||
private void Write(ArraySegment<byte> buffer)
|
||||
{
|
||||
Write(buffer.Array, buffer.Offset, buffer.Count);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureAdditionalCapacity(int additionalCapacity)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
{
|
||||
public enum MqttProtocolVersion
|
||||
{
|
||||
Unknown = 0,
|
||||
|
||||
V310 = 3,
|
||||
V311 = 4,
|
||||
V500 = 5
|
||||
|
||||
@@ -33,6 +33,15 @@ namespace MQTTnet.Formatter.V3
|
||||
};
|
||||
}
|
||||
|
||||
public MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket)
|
||||
{
|
||||
return new MqttPubAckPacket
|
||||
{
|
||||
PacketIdentifier = publishPacket.PacketIdentifier,
|
||||
ReasonCode = MqttPubAckReasonCode.Success
|
||||
};
|
||||
}
|
||||
|
||||
public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||
@@ -190,6 +199,7 @@ namespace MQTTnet.Formatter.V3
|
||||
{
|
||||
return new MqttClientPublishResult
|
||||
{
|
||||
PacketIdentifier = pubAckPacket?.PacketIdentifier,
|
||||
ReasonCode = MqttClientPublishReasonCode.Success
|
||||
};
|
||||
}
|
||||
@@ -206,6 +216,7 @@ namespace MQTTnet.Formatter.V3
|
||||
|
||||
return new MqttClientPublishResult
|
||||
{
|
||||
PacketIdentifier = pubCompPacket.PacketIdentifier,
|
||||
ReasonCode = MqttClientPublishReasonCode.Success
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Linq;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
@@ -11,8 +10,20 @@ namespace MQTTnet.Formatter.V3
|
||||
public class MqttV310PacketFormatter : IMqttPacketFormatter
|
||||
{
|
||||
private const int FixedHeaderSize = 1;
|
||||
|
||||
private readonly IMqttPacketWriter _packetWriter;
|
||||
|
||||
public MqttV310PacketFormatter()
|
||||
: this(new MqttPacketWriter())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MqttV310PacketFormatter(IMqttPacketWriter packetWriter)
|
||||
{
|
||||
_packetWriter = packetWriter;
|
||||
}
|
||||
|
||||
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
|
||||
|
||||
public IMqttDataConverter DataConverter { get; } = new MqttV310DataConverter();
|
||||
|
||||
@@ -27,17 +38,18 @@ namespace MQTTnet.Formatter.V3
|
||||
var fixedHeader = EncodePacket(packet, _packetWriter);
|
||||
var remainingLength = (uint)(_packetWriter.Length - 5);
|
||||
|
||||
var remainingLengthBuffer = MqttPacketWriter.EncodeVariableLengthInteger(remainingLength);
|
||||
var remainingLengthSize = MqttPacketWriter.GetLengthOfVariableInteger(remainingLength);
|
||||
|
||||
var headerSize = FixedHeaderSize + remainingLengthBuffer.Count;
|
||||
var headerSize = FixedHeaderSize + remainingLengthSize;
|
||||
var headerOffset = 5 - headerSize;
|
||||
|
||||
// Position cursor on correct offset on beginning of array (has leading 0x0)
|
||||
_packetWriter.Seek(headerOffset);
|
||||
_packetWriter.Write(fixedHeader);
|
||||
_packetWriter.Write(remainingLengthBuffer.Array, remainingLengthBuffer.Offset, remainingLengthBuffer.Count);
|
||||
_packetWriter.WriteVariableLengthInteger(remainingLength);
|
||||
|
||||
var buffer = _packetWriter.GetBuffer();
|
||||
|
||||
return new ArraySegment<byte>(buffer, headerOffset, _packetWriter.Length - headerOffset);
|
||||
}
|
||||
|
||||
@@ -56,17 +68,17 @@ namespace MQTTnet.Formatter.V3
|
||||
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Disconnect: return new MqttDisconnectPacket();
|
||||
case MqttControlPacketType.Publish: return DecodePublish(receivedMqttPacket);
|
||||
case MqttControlPacketType.PubAck: return DecodePubAck(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubRec: return DecodePubRec(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubRel: return DecodePubRel(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubComp: return DecodePubComp(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket);
|
||||
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PingReq: return new MqttPingReqPacket();
|
||||
case MqttControlPacketType.PingResp: return new MqttPingRespPacket();
|
||||
case MqttControlPacketType.Subscribe: return DecodeSubscribe(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.SubAck: return DecodeSubAck(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribe(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.UnsubAck: return DecodeUnsubAck(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body);
|
||||
|
||||
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
|
||||
}
|
||||
@@ -77,7 +89,7 @@ namespace MQTTnet.Formatter.V3
|
||||
_packetWriter.FreeBuffer();
|
||||
}
|
||||
|
||||
private byte EncodePacket(MqttBasePacket packet, MqttPacketWriter packetWriter)
|
||||
private byte EncodePacket(MqttBasePacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
switch (packet)
|
||||
{
|
||||
@@ -100,7 +112,7 @@ namespace MQTTnet.Formatter.V3
|
||||
}
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeUnsubAck(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeUnsubAckPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -110,7 +122,7 @@ namespace MQTTnet.Formatter.V3
|
||||
};
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubComp(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubCompPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -120,7 +132,7 @@ namespace MQTTnet.Formatter.V3
|
||||
};
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubRel(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubRelPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -130,7 +142,7 @@ namespace MQTTnet.Formatter.V3
|
||||
};
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubRec(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubRecPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -140,7 +152,7 @@ namespace MQTTnet.Formatter.V3
|
||||
};
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubAck(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubAckPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -150,7 +162,7 @@ namespace MQTTnet.Formatter.V3
|
||||
};
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeUnsubscribe(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeUnsubscribePacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -167,7 +179,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeSubscribe(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeSubscribePacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -190,7 +202,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePublish(ReceivedMqttPacket receivedMqttPacket)
|
||||
private static MqttBasePacket DecodePublishPacket(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(receivedMqttPacket.Body);
|
||||
|
||||
@@ -211,23 +223,36 @@ namespace MQTTnet.Formatter.V3
|
||||
PacketIdentifier = packetIdentifier,
|
||||
Retain = retain,
|
||||
Topic = topic,
|
||||
Payload = receivedMqttPacket.Body.ReadRemainingData().ToArray(),
|
||||
QualityOfServiceLevel = qualityOfServiceLevel,
|
||||
Dup = dup
|
||||
};
|
||||
|
||||
if (!receivedMqttPacket.Body.EndOfStream)
|
||||
{
|
||||
packet.Payload = receivedMqttPacket.Body.ReadRemainingData().ToArray();
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private MqttBasePacket DecodeConnectPacket(MqttPacketBodyReader body)
|
||||
private MqttBasePacket DecodeConnectPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
var packet = new MqttConnectPacket
|
||||
var protocolName = body.ReadStringWithLengthPrefix();
|
||||
var protocolVersion = body.ReadByte();
|
||||
|
||||
if (protocolName != "MQTT" && protocolName != "MQIsdp")
|
||||
{
|
||||
ProtocolName = body.ReadStringWithLengthPrefix(),
|
||||
ProtocolLevel = body.ReadByte()
|
||||
};
|
||||
throw new MqttProtocolViolationException("MQTT protocol name do not match MQTT v3.");
|
||||
}
|
||||
|
||||
if (protocolVersion != 3 && protocolVersion != 4)
|
||||
{
|
||||
throw new MqttProtocolViolationException("MQTT protocol version do not match MQTT v3.");
|
||||
}
|
||||
|
||||
var packet = new MqttConnectPacket();
|
||||
|
||||
var connectFlags = body.ReadByte();
|
||||
if ((connectFlags & 0x1) > 0)
|
||||
@@ -251,7 +276,7 @@ namespace MQTTnet.Formatter.V3
|
||||
packet.WillMessage = new MqttApplicationMessage
|
||||
{
|
||||
Topic = body.ReadStringWithLengthPrefix(),
|
||||
Payload = body.ReadWithLengthPrefix().ToArray(),
|
||||
Payload = body.ReadWithLengthPrefix(),
|
||||
QualityOfServiceLevel = (MqttQualityOfServiceLevel)willQoS,
|
||||
Retain = willRetain
|
||||
};
|
||||
@@ -271,7 +296,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeSubAck(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeSubAckPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -288,7 +313,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return packet;
|
||||
}
|
||||
|
||||
protected virtual MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
|
||||
protected virtual MqttBasePacket DecodeConnAckPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -319,7 +344,7 @@ namespace MQTTnet.Formatter.V3
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
|
||||
protected virtual byte EncodeConnectPacket(MqttConnectPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
ValidateConnectPacket(packet);
|
||||
|
||||
@@ -381,7 +406,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
}
|
||||
|
||||
protected virtual byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
|
||||
protected virtual byte EncodeConnAckPacket(MqttConnAckPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
packetWriter.Write(0); // Reserved.
|
||||
packetWriter.Write((byte)packet.ReturnCode.Value);
|
||||
@@ -389,7 +414,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
|
||||
}
|
||||
|
||||
private static byte EncodePubRelPacket(MqttPubRelPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -401,7 +426,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
||||
}
|
||||
|
||||
private static byte EncodePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePublishPacket(MqttPublishPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
ValidatePublishPacket(packet);
|
||||
|
||||
@@ -446,7 +471,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
|
||||
}
|
||||
|
||||
private static byte EncodePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -458,7 +483,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
|
||||
}
|
||||
|
||||
private static byte EncodePubRecPacket(MqttPubRecPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -470,7 +495,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
|
||||
}
|
||||
|
||||
private static byte EncodePubCompPacket(MqttPubCompPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -482,7 +507,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
|
||||
}
|
||||
|
||||
private static byte EncodeSubscribePacket(MqttSubscribePacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeSubscribePacket(MqttSubscribePacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
||||
|
||||
@@ -505,7 +530,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
|
||||
}
|
||||
|
||||
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -525,7 +550,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
|
||||
}
|
||||
|
||||
private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
|
||||
|
||||
@@ -547,7 +572,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
|
||||
}
|
||||
|
||||
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.PacketIdentifier.HasValue)
|
||||
{
|
||||
@@ -564,7 +589,7 @@ namespace MQTTnet.Formatter.V3
|
||||
}
|
||||
|
||||
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local
|
||||
protected static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body)
|
||||
protected static void ThrowIfBodyIsEmpty(IMqttPacketBodyReader body)
|
||||
{
|
||||
if (body == null || body.Length == 0)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,17 @@ namespace MQTTnet.Formatter.V3
|
||||
{
|
||||
public class MqttV311PacketFormatter : MqttV310PacketFormatter
|
||||
{
|
||||
protected override byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
|
||||
public MqttV311PacketFormatter()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public MqttV311PacketFormatter(IMqttPacketWriter packetWriter)
|
||||
: base(packetWriter)
|
||||
{
|
||||
}
|
||||
|
||||
protected override byte EncodeConnectPacket(MqttConnectPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
ValidateConnectPacket(packet);
|
||||
|
||||
@@ -68,7 +78,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
}
|
||||
|
||||
protected override byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
|
||||
protected override byte EncodeConnAckPacket(MqttConnAckPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
byte connectAcknowledgeFlags = 0x0;
|
||||
if (packet.IsSessionPresent)
|
||||
@@ -82,7 +92,7 @@ namespace MQTTnet.Formatter.V3
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
|
||||
}
|
||||
|
||||
protected override MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
|
||||
protected override MqttBasePacket DecodeConnAckPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
|
||||
@@ -46,6 +46,15 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
public MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket)
|
||||
{
|
||||
return new MqttPubAckPacket
|
||||
{
|
||||
PacketIdentifier = publishPacket.PacketIdentifier,
|
||||
ReasonCode = MqttPubAckReasonCode.Success
|
||||
};
|
||||
}
|
||||
|
||||
public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
|
||||
{
|
||||
return new MqttApplicationMessage
|
||||
@@ -180,13 +189,22 @@ namespace MQTTnet.Formatter.V5
|
||||
|
||||
public MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket)
|
||||
{
|
||||
var result = new MqttClientPublishResult();
|
||||
|
||||
if (pubAckPacket?.ReasonCode != null)
|
||||
var result = new MqttClientPublishResult
|
||||
{
|
||||
result.ReasonCode = (MqttClientPublishReasonCode)pubAckPacket.ReasonCode;
|
||||
}
|
||||
ReasonCode = MqttClientPublishReasonCode.Success,
|
||||
ReasonString = pubAckPacket?.Properties?.ReasonString,
|
||||
UserProperties = pubAckPacket?.Properties?.UserProperties
|
||||
};
|
||||
|
||||
if (pubAckPacket != null)
|
||||
{
|
||||
// QoS 0 has no response. So we treat it as a success always.
|
||||
// Both enums have the same values. So it can be easily converted.
|
||||
result.ReasonCode = (MqttClientPublishReasonCode)pubAckPacket.ReasonCode;
|
||||
|
||||
result.PacketIdentifier = pubAckPacket.PacketIdentifier;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -200,17 +218,24 @@ namespace MQTTnet.Formatter.V5
|
||||
};
|
||||
}
|
||||
|
||||
// The PUBCOMP is the last packet in QoS 2. So we use the results from that instead of PUBREC.
|
||||
if (pubCompPacket.ReasonCode == MqttPubCompReasonCode.PacketIdentifierNotFound)
|
||||
{
|
||||
return new MqttClientPublishResult
|
||||
{
|
||||
ReasonCode = MqttClientPublishReasonCode.UnspecifiedError
|
||||
PacketIdentifier = pubCompPacket.PacketIdentifier,
|
||||
ReasonCode = MqttClientPublishReasonCode.UnspecifiedError,
|
||||
ReasonString = pubCompPacket.Properties?.ReasonString,
|
||||
UserProperties = pubCompPacket.Properties?.UserProperties
|
||||
};
|
||||
}
|
||||
|
||||
var result = new MqttClientPublishResult
|
||||
{
|
||||
ReasonCode = MqttClientPublishReasonCode.Success
|
||||
PacketIdentifier = pubCompPacket.PacketIdentifier,
|
||||
ReasonCode = MqttClientPublishReasonCode.Success,
|
||||
ReasonString = pubCompPacket.Properties?.ReasonString,
|
||||
UserProperties = pubCompPacket.Properties?.UserProperties
|
||||
};
|
||||
|
||||
if (pubRecPacket.ReasonCode.HasValue)
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace MQTTnet.Formatter.V5
|
||||
}
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeConnectPacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeConnectPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace MQTTnet.Formatter.V5
|
||||
if (packet.WillMessage != null)
|
||||
{
|
||||
packet.WillMessage.Topic = body.ReadStringWithLengthPrefix();
|
||||
packet.WillMessage.Payload = body.ReadWithLengthPrefix().ToArray();
|
||||
packet.WillMessage.Payload = body.ReadWithLengthPrefix();
|
||||
}
|
||||
|
||||
if (usernameFlag)
|
||||
@@ -150,7 +150,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeConnAckPacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeConnAckPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -239,7 +239,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeDisconnectPacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeDisconnectPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -277,7 +277,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeSubscribePacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeSubscribePacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -327,7 +327,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeSubAckPacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeSubAckPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -363,7 +363,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeUnsubscribePacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeUnsubscribePacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -394,7 +394,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeUnsubAckPacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeUnsubAckPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -440,7 +440,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return new MqttPingRespPacket();
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePublishPacket(byte header, MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePublishPacket(byte header, IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -503,12 +503,15 @@ namespace MQTTnet.Formatter.V5
|
||||
}
|
||||
}
|
||||
|
||||
packet.Payload = body.ReadRemainingData().ToArray();
|
||||
if (!body.EndOfStream)
|
||||
{
|
||||
packet.Payload = body.ReadRemainingData().ToArray();
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubAckPacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubAckPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -546,7 +549,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubRecPacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubRecPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -584,7 +587,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubRelPacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubRelPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -622,7 +625,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodePubCompPacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodePubCompPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -660,7 +663,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DecodeAuthPacket(MqttPacketBodyReader body)
|
||||
private static MqttBasePacket DecodeAuthPacket(IMqttPacketBodyReader body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
|
||||
@@ -706,7 +709,7 @@ namespace MQTTnet.Formatter.V5
|
||||
}
|
||||
|
||||
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local
|
||||
private static void ThrowIfBodyIsEmpty(MqttPacketBodyReader body)
|
||||
private static void ThrowIfBodyIsEmpty(IMqttPacketBodyReader body)
|
||||
{
|
||||
if (body == null || body.Length == 0)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,19 @@ namespace MQTTnet.Formatter.V5
|
||||
{
|
||||
public class MqttV500PacketEncoder
|
||||
{
|
||||
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
|
||||
private readonly IMqttPacketWriter _packetWriter;
|
||||
|
||||
public MqttV500PacketEncoder()
|
||||
: this(new MqttPacketWriter())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MqttV500PacketEncoder(IMqttPacketWriter packetWriter)
|
||||
{
|
||||
_packetWriter = packetWriter;
|
||||
}
|
||||
|
||||
|
||||
public ArraySegment<byte> Encode(MqttBasePacket packet)
|
||||
{
|
||||
@@ -21,15 +33,15 @@ namespace MQTTnet.Formatter.V5
|
||||
var fixedHeader = EncodePacket(packet, _packetWriter);
|
||||
var remainingLength = (uint)(_packetWriter.Length - 5);
|
||||
|
||||
var remainingLengthBuffer = MqttPacketWriter.EncodeVariableLengthInteger(remainingLength);
|
||||
|
||||
var headerSize = 1 + remainingLengthBuffer.Count;
|
||||
var remainingLengthSize = MqttPacketWriter.GetLengthOfVariableInteger(remainingLength);
|
||||
|
||||
var headerSize = 1 + remainingLengthSize;
|
||||
var headerOffset = 5 - headerSize;
|
||||
|
||||
// Position cursor on correct offset on beginning of array (has leading 0x0)
|
||||
_packetWriter.Seek(headerOffset);
|
||||
_packetWriter.Write(fixedHeader);
|
||||
_packetWriter.Write(remainingLengthBuffer.Array, remainingLengthBuffer.Offset, remainingLengthBuffer.Count);
|
||||
_packetWriter.WriteVariableLengthInteger(remainingLength);
|
||||
|
||||
var buffer = _packetWriter.GetBuffer();
|
||||
return new ArraySegment<byte>(buffer, headerOffset, _packetWriter.Length - headerOffset);
|
||||
@@ -40,7 +52,7 @@ namespace MQTTnet.Formatter.V5
|
||||
_packetWriter.FreeBuffer();
|
||||
}
|
||||
|
||||
private static byte EncodePacket(MqttBasePacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePacket(MqttBasePacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
switch (packet)
|
||||
{
|
||||
@@ -64,7 +76,7 @@ namespace MQTTnet.Formatter.V5
|
||||
}
|
||||
}
|
||||
|
||||
private static byte EncodeConnectPacket(MqttConnectPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeConnectPacket(MqttConnectPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||
@@ -150,7 +162,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
}
|
||||
|
||||
private static byte EncodeConnAckPacket(MqttConnAckPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeConnAckPacket(MqttConnAckPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||
@@ -195,7 +207,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
|
||||
}
|
||||
|
||||
private static byte EncodePublishPacket(MqttPublishPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePublishPacket(MqttPublishPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||
@@ -261,7 +273,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
|
||||
}
|
||||
|
||||
private static byte EncodePubAckPacket(MqttPubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||
@@ -273,7 +285,7 @@ namespace MQTTnet.Formatter.V5
|
||||
|
||||
if (!packet.ReasonCode.HasValue)
|
||||
{
|
||||
throw new MqttProtocolViolationException("PubAck packet must contain a connect reason.");
|
||||
throw new MqttProtocolViolationException("PubAck packet must contain a reason code.");
|
||||
}
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
@@ -294,7 +306,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
|
||||
}
|
||||
|
||||
private static byte EncodePubRecPacket(MqttPubRecPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
@@ -322,7 +334,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
|
||||
}
|
||||
|
||||
private static byte EncodePubRelPacket(MqttPubRelPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
@@ -350,7 +362,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
||||
}
|
||||
|
||||
private static byte EncodePubCompPacket(MqttPubCompPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
@@ -378,9 +390,9 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
|
||||
}
|
||||
|
||||
private static byte EncodeSubscribePacket(MqttSubscribePacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeSubscribePacket(MqttSubscribePacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
||||
if (packet.TopicFilters?.Any() != true) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
||||
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
@@ -425,14 +437,11 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
|
||||
}
|
||||
|
||||
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
if (packet.ReasonCodes?.Any() != true) throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
|
||||
|
||||
if (packet.ReasonCodes == null || !packet.ReasonCodes.Any())
|
||||
{
|
||||
throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
|
||||
}
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
|
||||
@@ -453,9 +462,9 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
|
||||
}
|
||||
|
||||
private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
|
||||
if (packet.TopicFilters?.Any() != true) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
|
||||
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
@@ -477,15 +486,12 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
|
||||
}
|
||||
|
||||
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packet.ReasonCodes?.Any() != true) throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
|
||||
|
||||
ThrowIfPacketIdentifierIsInvalid(packet);
|
||||
|
||||
if (packet.ReasonCodes == null || !packet.ReasonCodes.Any())
|
||||
{
|
||||
throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
|
||||
}
|
||||
|
||||
|
||||
packetWriter.Write(packet.PacketIdentifier.Value);
|
||||
|
||||
var propertiesWriter = new MqttV500PropertiesWriter();
|
||||
@@ -505,7 +511,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
|
||||
}
|
||||
|
||||
private static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (!packet.ReasonCode.HasValue)
|
||||
{
|
||||
@@ -538,7 +544,7 @@ namespace MQTTnet.Formatter.V5
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingResp);
|
||||
}
|
||||
|
||||
private static byte EncodeAuthPacket(MqttAuthPacket packet, MqttPacketWriter packetWriter)
|
||||
private static byte EncodeAuthPacket(MqttAuthPacket packet, IMqttPacketWriter packetWriter)
|
||||
{
|
||||
packetWriter.Write((byte)packet.ReasonCode);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
@@ -7,9 +6,19 @@ namespace MQTTnet.Formatter.V5
|
||||
{
|
||||
public class MqttV500PacketFormatter : IMqttPacketFormatter
|
||||
{
|
||||
private readonly MqttV500PacketEncoder _encoder = new MqttV500PacketEncoder();
|
||||
private readonly MqttV500PacketEncoder _encoder;
|
||||
private readonly MqttV500PacketDecoder _decoder = new MqttV500PacketDecoder();
|
||||
|
||||
public MqttV500PacketFormatter()
|
||||
{
|
||||
_encoder = new MqttV500PacketEncoder();
|
||||
}
|
||||
|
||||
public MqttV500PacketFormatter(IMqttPacketWriter writer)
|
||||
{
|
||||
_encoder = new MqttV500PacketEncoder(writer);
|
||||
}
|
||||
|
||||
public IMqttDataConverter DataConverter { get; } = new MqttV500DataConverter();
|
||||
|
||||
public ArraySegment<byte> Encode(MqttBasePacket mqttPacket)
|
||||
|
||||
@@ -9,11 +9,11 @@ namespace MQTTnet.Formatter.V5
|
||||
{
|
||||
public class MqttV500PropertiesReader
|
||||
{
|
||||
private readonly MqttPacketBodyReader _body;
|
||||
private readonly IMqttPacketBodyReader _body;
|
||||
private readonly uint _length;
|
||||
private readonly ulong _targetOffset;
|
||||
|
||||
public MqttV500PropertiesReader(MqttPacketBodyReader body)
|
||||
public MqttV500PropertiesReader(IMqttPacketBodyReader body)
|
||||
{
|
||||
_body = body ?? throw new ArgumentNullException(nameof(body));
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace MQTTnet.Formatter.V5
|
||||
|
||||
public byte[] ReadAuthenticationData()
|
||||
{
|
||||
return _body.ReadWithLengthPrefix().ToArray();
|
||||
return _body.ReadWithLengthPrefix();
|
||||
}
|
||||
|
||||
public bool? ReadRetainAvailable()
|
||||
@@ -165,7 +165,7 @@ namespace MQTTnet.Formatter.V5
|
||||
|
||||
public byte[] ReadCorrelationData()
|
||||
{
|
||||
return _body.ReadWithLengthPrefix().ToArray();
|
||||
return _body.ReadWithLengthPrefix();
|
||||
}
|
||||
|
||||
public string ReadContentType()
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace MQTTnet.Formatter.V5
|
||||
Write(MqttPropertyId.AuthenticationMethod, value);
|
||||
}
|
||||
|
||||
public void WriteToPacket(MqttPacketWriter packetWriter)
|
||||
public void WriteToPacket(IMqttPacketWriter packetWriter)
|
||||
{
|
||||
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Security.Authentication;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Networking;
|
||||
@@ -38,22 +39,13 @@ namespace MQTTnet.Implementations
|
||||
CreateStreams();
|
||||
|
||||
IsSecureConnection = socket.Information.ProtectionLevel >= SocketProtectionLevel.Tls12;
|
||||
|
||||
Endpoint = _socket.Information.RemoteAddress + ":" + _socket.Information.RemotePort;
|
||||
}
|
||||
|
||||
public static Func<MqttClientTcpOptions, IEnumerable<ChainValidationResult>> CustomIgnorableServerCertificateErrorsResolver { get; set; }
|
||||
|
||||
public string Endpoint
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_socket?.Information != null)
|
||||
{
|
||||
return _socket.Information.RemoteAddress + ":" + _socket.Information.RemotePort;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public string Endpoint { get; private set; }
|
||||
|
||||
public bool IsSecureConnection { get; }
|
||||
|
||||
@@ -62,11 +54,11 @@ namespace MQTTnet.Implementations
|
||||
if (_socket == null)
|
||||
{
|
||||
_socket = new StreamSocket();
|
||||
_socket.Control.NoDelay = true;
|
||||
_socket.Control.NoDelay = _options.NoDelay;
|
||||
_socket.Control.KeepAlive = true;
|
||||
}
|
||||
|
||||
if (!_options.TlsOptions.UseTls)
|
||||
if (_options.TlsOptions?.UseTls != true)
|
||||
{
|
||||
await _socket.ConnectAsync(new HostName(_options.Server), _options.GetPort().ToString());
|
||||
}
|
||||
@@ -79,9 +71,21 @@ namespace MQTTnet.Implementations
|
||||
_socket.Control.IgnorableServerCertificateErrors.Add(ignorableChainValidationResult);
|
||||
}
|
||||
|
||||
await _socket.ConnectAsync(new HostName(_options.Server), _options.GetPort().ToString(), SocketProtectionLevel.Tls12);
|
||||
var socketProtectionLevel = SocketProtectionLevel.Tls12;
|
||||
if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
|
||||
{
|
||||
socketProtectionLevel = SocketProtectionLevel.Tls11;
|
||||
}
|
||||
else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls)
|
||||
{
|
||||
socketProtectionLevel = SocketProtectionLevel.Tls10;
|
||||
}
|
||||
|
||||
await _socket.ConnectAsync(new HostName(_options.Server), _options.GetPort().ToString(), socketProtectionLevel);
|
||||
}
|
||||
|
||||
Endpoint = _socket.Information.RemoteAddress + ":" + _socket.Information.RemotePort;
|
||||
|
||||
CreateStreams();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client.Options;
|
||||
@@ -17,12 +18,8 @@ namespace MQTTnet.Implementations
|
||||
private readonly IMqttClientOptions _clientOptions;
|
||||
private readonly MqttClientTcpOptions _options;
|
||||
|
||||
private Socket _socket;
|
||||
private Stream _stream;
|
||||
|
||||
/// <summary>
|
||||
/// called on client sockets are created in connect
|
||||
/// </summary>
|
||||
public MqttTcpChannel(IMqttClientOptions clientOptions)
|
||||
{
|
||||
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
|
||||
@@ -31,48 +28,61 @@ namespace MQTTnet.Implementations
|
||||
IsSecureConnection = clientOptions.ChannelOptions?.TlsOptions?.UseTls == true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// called on server, sockets are passed in
|
||||
/// connect will not be called
|
||||
/// </summary>
|
||||
public MqttTcpChannel(Socket socket, SslStream sslStream)
|
||||
public MqttTcpChannel(Stream stream, string endpoint)
|
||||
{
|
||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
|
||||
|
||||
IsSecureConnection = sslStream != null;
|
||||
|
||||
CreateStream(sslStream);
|
||||
IsSecureConnection = stream is SslStream;
|
||||
Endpoint = endpoint;
|
||||
}
|
||||
|
||||
public string Endpoint => _socket?.RemoteEndPoint?.ToString();
|
||||
public string Endpoint { get; private set; }
|
||||
|
||||
public bool IsSecureConnection { get; }
|
||||
|
||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_socket == null)
|
||||
Socket socket;
|
||||
|
||||
if (_options.AddressFamily == AddressFamily.Unspecified)
|
||||
{
|
||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
|
||||
socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
}
|
||||
else
|
||||
{
|
||||
socket = new Socket(_options.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
}
|
||||
|
||||
socket.ReceiveBufferSize = _options.BufferSize;
|
||||
socket.SendBufferSize = _options.BufferSize;
|
||||
socket.DualMode = _options.DualMode;
|
||||
socket.NoDelay = _options.NoDelay;
|
||||
|
||||
// Workaround for: workaround for https://github.com/dotnet/corefx/issues/24430
|
||||
using (cancellationToken.Register(() => _socket.Dispose()))
|
||||
using (cancellationToken.Register(() => socket.Dispose()))
|
||||
{
|
||||
#if NET452 || NET461
|
||||
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, _options.Server, _options.GetPort(), null).ConfigureAwait(false);
|
||||
await Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, _options.Server, _options.GetPort(), null).ConfigureAwait(false);
|
||||
#else
|
||||
await _socket.ConnectAsync(_options.Server, _options.GetPort()).ConfigureAwait(false);
|
||||
await socket.ConnectAsync(_options.Server, _options.GetPort()).ConfigureAwait(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
SslStream sslStream = null;
|
||||
var networkStream = new NetworkStream(socket, true);
|
||||
|
||||
if (_options.TlsOptions.UseTls)
|
||||
{
|
||||
sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
|
||||
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
||||
await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||
|
||||
_stream = sslStream;
|
||||
}
|
||||
else
|
||||
{
|
||||
_stream = networkStream;
|
||||
}
|
||||
|
||||
CreateStream(sslStream);
|
||||
Endpoint = socket.RemoteEndPoint?.ToString();
|
||||
}
|
||||
|
||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||
@@ -83,37 +93,58 @@ namespace MQTTnet.Implementations
|
||||
|
||||
public async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||
using (cancellationToken.Register(Dispose))
|
||||
try
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||
using (cancellationToken.Register(Dispose))
|
||||
{
|
||||
return 0;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return await _stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
if (exception.InnerException is SocketException socketException)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(socketException).Throw();
|
||||
}
|
||||
|
||||
return await _stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||
using (cancellationToken.Register(Dispose))
|
||||
try
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
// Workaround for: https://github.com/dotnet/corefx/issues/24430
|
||||
using (cancellationToken.Register(Dispose))
|
||||
{
|
||||
return;
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (IOException exception)
|
||||
{
|
||||
if (exception.InnerException is SocketException socketException)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(socketException).Throw();
|
||||
}
|
||||
|
||||
await _stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||
//await _stream.FlushAsync(cancellationToken);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_socket = null;
|
||||
|
||||
// When the stream is disposed it will also close the socket and this will also dispose it.
|
||||
// So there is no need to dispose the socket again.
|
||||
// https://stackoverflow.com/questions/3601521/should-i-manually-dispose-the-socket-after-closing-it
|
||||
@@ -127,6 +158,8 @@ namespace MQTTnet.Implementations
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
}
|
||||
|
||||
_stream = null;
|
||||
}
|
||||
|
||||
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
@@ -175,18 +208,6 @@ namespace MQTTnet.Implementations
|
||||
|
||||
return certificates;
|
||||
}
|
||||
|
||||
private void CreateStream(Stream stream)
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
_stream = stream;
|
||||
}
|
||||
else
|
||||
{
|
||||
_stream = new NetworkStream(_socket, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace MQTTnet.Implementations
|
||||
_logger = logger.CreateChildLogger(nameof(MqttTcpServerAdapter));
|
||||
}
|
||||
|
||||
public Action<MqttServerAdapterClientAcceptedEventArgs> ClientAcceptedHandler { get; set; }
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public async Task StartAsync(IMqttServerOptions options)
|
||||
{
|
||||
@@ -36,10 +36,10 @@ namespace MQTTnet.Implementations
|
||||
_listener = new StreamSocketListener();
|
||||
|
||||
// This also affects the client sockets.
|
||||
_listener.Control.NoDelay = true;
|
||||
_listener.Control.NoDelay = options.DefaultEndpointOptions.NoDelay;
|
||||
_listener.Control.KeepAlive = true;
|
||||
_listener.Control.QualityOfService = SocketQualityOfService.LowLatency;
|
||||
_listener.ConnectionReceived += AcceptDefaultEndpointConnectionsAsync;
|
||||
_listener.ConnectionReceived += OnConnectionReceivedAsync;
|
||||
|
||||
await _listener.BindServiceNameAsync(options.DefaultEndpointOptions.Port.ToString(), SocketProtectionLevel.PlainSocket);
|
||||
}
|
||||
@@ -54,30 +54,51 @@ namespace MQTTnet.Implementations
|
||||
{
|
||||
if (_listener != null)
|
||||
{
|
||||
_listener.ConnectionReceived -= AcceptDefaultEndpointConnectionsAsync;
|
||||
_listener.ConnectionReceived -= OnConnectionReceivedAsync;
|
||||
}
|
||||
|
||||
_listener?.Dispose();
|
||||
_listener = null;
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopAsync().GetAwaiter().GetResult();
|
||||
_listener?.Dispose();
|
||||
_listener = null;
|
||||
}
|
||||
|
||||
private void AcceptDefaultEndpointConnectionsAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
|
||||
private async void OnConnectionReceivedAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketFormatterAdapter(), _logger);
|
||||
ClientAcceptedHandler?.Invoke(new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketFormatterAdapter(), _logger))
|
||||
{
|
||||
await clientHandler(clientAdapter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while accepting connection at default endpoint.");
|
||||
if (exception is ObjectDisposedException)
|
||||
{
|
||||
// It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed.
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Error(exception, "Error while handling client connection.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
args.Socket.Dispose();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while cleaning up client connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace MQTTnet.Implementations
|
||||
_logger = logger.CreateChildLogger(nameof(MqttTcpServerAdapter));
|
||||
}
|
||||
|
||||
public Action<MqttServerAdapterClientAcceptedEventArgs> ClientAcceptedHandler { get; set; }
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public Task StartAsync(IMqttServerOptions options)
|
||||
{
|
||||
@@ -89,7 +89,7 @@ namespace MQTTnet.Implementations
|
||||
_cancellationTokenSource.Token,
|
||||
_logger)
|
||||
{
|
||||
ClientAcceptedHandler = OnClientAccepted
|
||||
ClientHandler = OnClientAcceptedAsync
|
||||
};
|
||||
|
||||
listenerV4.Start();
|
||||
@@ -105,7 +105,7 @@ namespace MQTTnet.Implementations
|
||||
_cancellationTokenSource.Token,
|
||||
_logger)
|
||||
{
|
||||
ClientAcceptedHandler = OnClientAccepted
|
||||
ClientHandler = OnClientAcceptedAsync
|
||||
};
|
||||
|
||||
listenerV6.Start();
|
||||
@@ -113,9 +113,15 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClientAccepted(MqttServerAdapterClientAcceptedEventArgs eventArgs)
|
||||
private Task OnClientAcceptedAsync(IMqttChannelAdapter channelAdapter)
|
||||
{
|
||||
ClientAcceptedHandler?.Invoke(eventArgs);
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler == null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
return clientHandler(channelAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#if !WINDOWS_UWP
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
@@ -35,14 +36,14 @@ namespace MQTTnet.Implementations
|
||||
_tlsCertificate = tlsCertificate;
|
||||
_cancellationToken = cancellationToken;
|
||||
_logger = logger.CreateChildLogger(nameof(MqttTcpServerListener));
|
||||
|
||||
|
||||
if (_options is MqttServerTlsTcpEndpointOptions tlsOptions)
|
||||
{
|
||||
_tlsOptions = tlsOptions;
|
||||
}
|
||||
}
|
||||
|
||||
public Action<MqttServerAdapterClientAcceptedEventArgs> ClientAcceptedHandler;
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public void Start()
|
||||
{
|
||||
@@ -58,53 +59,7 @@ namespace MQTTnet.Implementations
|
||||
_logger.Info($"Starting TCP listener for {_socket.LocalEndPoint} TLS={_tlsCertificate != null}.");
|
||||
|
||||
_socket.Listen(_options.ConnectionBacklog);
|
||||
Task.Run(AcceptClientConnectionsAsync, _cancellationToken);
|
||||
}
|
||||
|
||||
private async Task AcceptClientConnectionsAsync()
|
||||
{
|
||||
while (!_cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if NET452 || NET461
|
||||
var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null).ConfigureAwait(false);
|
||||
#else
|
||||
var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false);
|
||||
#endif
|
||||
clientSocket.NoDelay = true;
|
||||
|
||||
SslStream sslStream = null;
|
||||
|
||||
if (_tlsCertificate != null)
|
||||
{
|
||||
sslStream = new SslStream(new NetworkStream(clientSocket), false);
|
||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, _tlsOptions.SslProtocol, false).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.",
|
||||
clientSocket.RemoteEndPoint,
|
||||
_socket.LocalEndPoint,
|
||||
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
||||
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketFormatterAdapter(), _logger);
|
||||
ClientAcceptedHandler?.Invoke(new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed.
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is SocketException s && s.SocketErrorCode == SocketError.OperationAborted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Error(exception, $"Error while accepting connection at TCP listener {_socket.LocalEndPoint} TLS={_tlsCertificate != null}.");
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), _cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
Task.Run(() => AcceptClientConnectionsAsync(_cancellationToken), _cancellationToken);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -115,6 +70,98 @@ namespace MQTTnet.Implementations
|
||||
_tlsCertificate?.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
private async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if NET452 || NET461
|
||||
var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null).ConfigureAwait(false);
|
||||
#else
|
||||
var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false);
|
||||
#endif
|
||||
#pragma warning disable 4014
|
||||
Task.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken);
|
||||
#pragma warning restore 4014
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, $"Error while accepting connection at TCP listener {_socket.LocalEndPoint} TLS={_tlsCertificate != null}.");
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TryHandleClientConnectionAsync(Socket clientSocket)
|
||||
{
|
||||
Stream stream = null;
|
||||
string remoteEndPoint = null;
|
||||
|
||||
try
|
||||
{
|
||||
remoteEndPoint = clientSocket.RemoteEndPoint.ToString();
|
||||
|
||||
_logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.",
|
||||
remoteEndPoint,
|
||||
_socket.LocalEndPoint,
|
||||
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
||||
|
||||
clientSocket.NoDelay = _options.NoDelay;
|
||||
|
||||
stream = new NetworkStream(clientSocket, true);
|
||||
|
||||
if (_tlsCertificate != null)
|
||||
{
|
||||
var sslStream = new SslStream(stream, false);
|
||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, _tlsOptions.SslProtocol, false).ConfigureAwait(false);
|
||||
stream = sslStream;
|
||||
}
|
||||
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(stream, remoteEndPoint), new MqttPacketFormatterAdapter(), _logger))
|
||||
{
|
||||
await clientHandler(clientAdapter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is ObjectDisposedException)
|
||||
{
|
||||
// It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (exception is SocketException socketException &&
|
||||
socketException.SocketErrorCode == SocketError.OperationAborted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Error(exception, "Error while handling client connection.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
stream?.Dispose();
|
||||
clientSocket?.Dispose();
|
||||
|
||||
_logger.Verbose("Client '{0}' disconnected at TCP listener '{1}, {2}'.",
|
||||
remoteEndPoint,
|
||||
_socket.LocalEndPoint,
|
||||
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
||||
}
|
||||
catch (Exception disposeException)
|
||||
{
|
||||
_logger.Error(disposeException, "Error while cleaning up client connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
@@ -8,7 +8,10 @@ namespace MQTTnet.Internal
|
||||
public sealed class AsyncQueue<TItem> : IDisposable
|
||||
{
|
||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0);
|
||||
private readonly ConcurrentQueue<TItem> _queue = new ConcurrentQueue<TItem>();
|
||||
|
||||
private ConcurrentQueue<TItem> _queue = new ConcurrentQueue<TItem>();
|
||||
|
||||
public int Count => _queue.Count;
|
||||
|
||||
public void Enqueue(TItem item)
|
||||
{
|
||||
@@ -16,9 +19,9 @@ namespace MQTTnet.Internal
|
||||
_semaphore.Release();
|
||||
}
|
||||
|
||||
public async Task<TItem> DequeueAsync(CancellationToken cancellationToken)
|
||||
public async Task<AsyncQueueDequeueResult<TItem>> TryDequeueAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (true)
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -26,9 +29,26 @@ namespace MQTTnet.Internal
|
||||
|
||||
if (_queue.TryDequeue(out var item))
|
||||
{
|
||||
return item;
|
||||
return new AsyncQueueDequeueResult<TItem>(true, item);
|
||||
}
|
||||
}
|
||||
|
||||
return new AsyncQueueDequeueResult<TItem>(false, default(TItem));
|
||||
}
|
||||
|
||||
public AsyncQueueDequeueResult<TItem> TryDequeue()
|
||||
{
|
||||
if (_queue.TryDequeue(out var item))
|
||||
{
|
||||
return new AsyncQueueDequeueResult<TItem>(true, item);
|
||||
}
|
||||
|
||||
return new AsyncQueueDequeueResult<TItem>(false, default(TItem));
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Interlocked.Exchange(ref _queue, new ConcurrentQueue<TItem>());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace MQTTnet.Internal
|
||||
{
|
||||
public class AsyncQueueDequeueResult<TItem>
|
||||
{
|
||||
public AsyncQueueDequeueResult(bool isSuccess, TItem item)
|
||||
{
|
||||
IsSuccess = isSuccess;
|
||||
Item = item;
|
||||
}
|
||||
|
||||
public bool IsSuccess { get; }
|
||||
|
||||
public TItem Item { get; }
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='uap10.0'">
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.1.9" />
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net452'">
|
||||
@@ -62,9 +62,5 @@
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net461'">
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -14,7 +14,7 @@ namespace MQTTnet
|
||||
|
||||
public bool Retain { get; set; }
|
||||
|
||||
public List<MqttUserProperty> UserProperties { get; set; } = new List<MqttUserProperty>();
|
||||
public List<MqttUserProperty> UserProperties { get; set; }
|
||||
|
||||
public string ContentType { get; set; }
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -11,8 +10,6 @@ namespace MQTTnet
|
||||
{
|
||||
public class MqttApplicationMessageBuilder
|
||||
{
|
||||
private readonly List<MqttUserProperty> _userProperties = new List<MqttUserProperty>();
|
||||
|
||||
private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
||||
private string _topic;
|
||||
private byte[] _payload;
|
||||
@@ -24,6 +21,7 @@ namespace MQTTnet
|
||||
private uint? _subscriptionIdentifier;
|
||||
private uint? _messageExpiryInterval;
|
||||
private MqttPayloadFormatIndicator? _payloadFormatIndicator;
|
||||
private List<MqttUserProperty> _userProperties;
|
||||
|
||||
public MqttApplicationMessageBuilder WithTopic(string topic)
|
||||
{
|
||||
@@ -70,7 +68,7 @@ namespace MQTTnet
|
||||
|
||||
if (payload.Length == 0)
|
||||
{
|
||||
_payload = new byte[0];
|
||||
_payload = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -89,7 +87,7 @@ namespace MQTTnet
|
||||
return this;
|
||||
}
|
||||
|
||||
_payload = string.IsNullOrEmpty(payload) ? new byte[0] : Encoding.UTF8.GetBytes(payload);
|
||||
_payload = string.IsNullOrEmpty(payload) ? null : Encoding.UTF8.GetBytes(payload);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -128,6 +126,11 @@ namespace MQTTnet
|
||||
/// </summary>
|
||||
public MqttApplicationMessageBuilder WithUserProperty(string name, string value)
|
||||
{
|
||||
if (_userProperties == null)
|
||||
{
|
||||
_userProperties = new List<MqttUserProperty>();
|
||||
}
|
||||
|
||||
_userProperties.Add(new MqttUserProperty(name, value));
|
||||
return this;
|
||||
}
|
||||
@@ -205,7 +208,7 @@ namespace MQTTnet
|
||||
var applicationMessage = new MqttApplicationMessage
|
||||
{
|
||||
Topic = _topic,
|
||||
Payload = _payload ?? new byte[0],
|
||||
Payload = _payload,
|
||||
QualityOfServiceLevel = _qualityOfServiceLevel,
|
||||
Retain = _retain,
|
||||
ContentType = _contentType,
|
||||
@@ -217,8 +220,11 @@ namespace MQTTnet
|
||||
PayloadFormatIndicator = _payloadFormatIndicator
|
||||
};
|
||||
|
||||
applicationMessage.UserProperties.AddRange(_userProperties);
|
||||
|
||||
if (_userProperties?.Any() == true)
|
||||
{
|
||||
applicationMessage.UserProperties = _userProperties;
|
||||
}
|
||||
|
||||
return applicationMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
{
|
||||
public class MqttConnectPacket : MqttBasePacket
|
||||
{
|
||||
public string ProtocolName { get; set; }
|
||||
|
||||
public byte? ProtocolLevel { get; set; }
|
||||
|
||||
public string ClientId { get; set; }
|
||||
|
||||
public string Username { get; set; }
|
||||
@@ -33,7 +29,7 @@
|
||||
password = "****";
|
||||
}
|
||||
|
||||
return string.Concat("Connect: [ProtocolLevel=", ProtocolLevel, "] [ClientId=", ClientId, "] [Username=", Username, "] [Password=", password, "] [KeepAlivePeriod=", KeepAlivePeriod, "] [CleanSession=", CleanSession, "]");
|
||||
return string.Concat("Connect: [ClientId=", ClientId, "] [Username=", Username, "] [Password=", password, "] [KeepAlivePeriod=", KeepAlivePeriod, "] [CleanSession=", CleanSession, "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ namespace MQTTnet.Server
|
||||
Task<IList<IMqttClientStatus>> GetClientStatusAsync();
|
||||
Task<IList<IMqttSessionStatus>> GetSessionStatusAsync();
|
||||
|
||||
Task<IList<MqttApplicationMessage>> GetRetainedMessagesAsync();
|
||||
Task ClearRetainedMessagesAsync();
|
||||
Task<IList<MqttApplicationMessage>> GetRetainedApplicationMessagesAsync();
|
||||
Task ClearRetainedApplicationMessagesAsync();
|
||||
|
||||
Task SubscribeAsync(string clientId, ICollection<TopicFilter> topicFilters);
|
||||
Task UnsubscribeAsync(string clientId, ICollection<string> topicFilters);
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace MQTTnet.Server
|
||||
{
|
||||
public interface IMqttServerOptions
|
||||
{
|
||||
string ClientId { get; set; }
|
||||
|
||||
bool EnablePersistentSessions { get; }
|
||||
|
||||
int MaxPendingMessagesPerClient { get; }
|
||||
@@ -19,6 +21,6 @@ namespace MQTTnet.Server
|
||||
MqttServerTcpEndpointOptions DefaultEndpointOptions { get; }
|
||||
MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; }
|
||||
|
||||
IMqttServerStorage Storage { get; }
|
||||
IMqttServerStorage Storage { get; }
|
||||
}
|
||||
}
|
||||
@@ -27,15 +27,17 @@ namespace MQTTnet.Server
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
private readonly IMqttServerOptions _serverOptions;
|
||||
|
||||
private Task<MqttClientDisconnectType> _packageReceiverTask;
|
||||
private readonly IMqttChannelAdapter _channelAdapter;
|
||||
private readonly IMqttDataConverter _dataConverter;
|
||||
private readonly string _endpoint;
|
||||
private readonly MqttConnectPacket _connectPacket;
|
||||
|
||||
private Task<MqttClientDisconnectType> _packageReceiverTask;
|
||||
private DateTime _lastPacketReceivedTimestamp;
|
||||
private DateTime _lastNonKeepAlivePacketReceivedTimestamp;
|
||||
|
||||
private long _receivedPacketsCount;
|
||||
private long _sentPacketsCount;
|
||||
private long _sentPacketsCount = 1; // Start with 1 because the CONNECT packet is not counted anywhere.
|
||||
private long _receivedApplicationMessagesCount;
|
||||
private long _sentApplicationMessagesCount;
|
||||
|
||||
@@ -64,6 +66,7 @@ namespace MQTTnet.Server
|
||||
_keepAliveMonitor = new MqttClientKeepAliveMonitor(this, _logger);
|
||||
|
||||
_lastPacketReceivedTimestamp = DateTime.UtcNow;
|
||||
_lastNonKeepAlivePacketReceivedTimestamp = _lastPacketReceivedTimestamp;
|
||||
}
|
||||
|
||||
public string ClientId => _connectPacket.ClientId;
|
||||
@@ -75,7 +78,7 @@ namespace MQTTnet.Server
|
||||
StopInternal();
|
||||
|
||||
var task = _packageReceiverTask;
|
||||
if (task != null && !task.IsCompleted)
|
||||
if (task != null)
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
@@ -85,7 +88,7 @@ namespace MQTTnet.Server
|
||||
{
|
||||
status.ClientId = ClientId;
|
||||
status.Endpoint = _endpoint;
|
||||
status.ProtocolVersion = _channelAdapter.PacketFormatterAdapter.ProtocolVersion.Value;
|
||||
status.ProtocolVersion = _channelAdapter.PacketFormatterAdapter.ProtocolVersion;
|
||||
|
||||
status.ReceivedApplicationMessagesCount = Interlocked.Read(ref _receivedApplicationMessagesCount);
|
||||
status.SentApplicationMessagesCount = Interlocked.Read(ref _sentApplicationMessagesCount);
|
||||
@@ -94,17 +97,12 @@ namespace MQTTnet.Server
|
||||
status.SentPacketsCount = Interlocked.Read(ref _sentPacketsCount);
|
||||
|
||||
status.LastPacketReceivedTimestamp = _lastPacketReceivedTimestamp;
|
||||
status.LastNonKeepAlivePacketReceivedTimestamp = _lastNonKeepAlivePacketReceivedTimestamp;
|
||||
|
||||
//status.LastNonKeepAlivePacketReceived = _keepAliveMonitor.LastNonKeepAlivePacketReceived;
|
||||
status.BytesSent = _channelAdapter.BytesSent;
|
||||
status.BytesReceived = _channelAdapter.BytesReceived;
|
||||
}
|
||||
|
||||
//public void ClearPendingApplicationMessages()
|
||||
//{
|
||||
// Session.ApplicationMessagesQueue.Clear();
|
||||
|
||||
// //_applicationMessagesQueue.Clear();
|
||||
//}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationToken.Dispose();
|
||||
@@ -116,25 +114,15 @@ namespace MQTTnet.Server
|
||||
return _packageReceiverTask;
|
||||
}
|
||||
|
||||
public void EnqueueApplicationMessage(MqttApplicationMessage applicationMessage, string senderClientId, bool isRetainedApplicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
Session.EnqueueApplicationMessage(applicationMessage, senderClientId, isRetainedApplicationMessage);
|
||||
|
||||
_logger.Verbose("Enqueued application message (ClientId: {0}).", ClientId);
|
||||
}
|
||||
|
||||
private async Task<MqttClientDisconnectType> RunInternalAsync()
|
||||
{
|
||||
var disconnectType = MqttClientDisconnectType.NotClean;
|
||||
try
|
||||
{
|
||||
_logger.Info("Client '{0}': Session started.", ClientId);
|
||||
//_eventDispatcher.OnClientConnected(ClientId);
|
||||
|
||||
_channelAdapter.ReadingPacketStarted += OnAdapterReadingPacketStarted;
|
||||
_channelAdapter.ReadingPacketCompleted += OnAdapterReadingPacketCompleted;
|
||||
|
||||
_channelAdapter.ReadingPacketStartedCallback = OnAdapterReadingPacketStarted;
|
||||
_channelAdapter.ReadingPacketCompletedCallback = OnAdapterReadingPacketCompleted;
|
||||
|
||||
Session.WillMessage = _connectPacket.WillMessage;
|
||||
|
||||
@@ -166,7 +154,12 @@ namespace MQTTnet.Server
|
||||
|
||||
Interlocked.Increment(ref _sentPacketsCount);
|
||||
_lastPacketReceivedTimestamp = DateTime.UtcNow;
|
||||
|
||||
|
||||
if (!(packet is MqttPingReqPacket || packet is MqttPingRespPacket))
|
||||
{
|
||||
_lastNonKeepAlivePacketReceivedTimestamp = _lastPacketReceivedTimestamp;
|
||||
}
|
||||
|
||||
_keepAliveMonitor.PacketReceived();
|
||||
|
||||
if (packet is MqttPublishPacket publishPacket)
|
||||
@@ -243,12 +236,11 @@ namespace MQTTnet.Server
|
||||
|
||||
_packetDispatcher.Reset();
|
||||
|
||||
_channelAdapter.ReadingPacketStarted -= OnAdapterReadingPacketStarted;
|
||||
_channelAdapter.ReadingPacketCompleted -= OnAdapterReadingPacketCompleted;
|
||||
_channelAdapter.ReadingPacketStartedCallback = null;
|
||||
_channelAdapter.ReadingPacketCompletedCallback = null;
|
||||
|
||||
_logger.Info("Client '{0}': Session stopped.", ClientId);
|
||||
//_eventDispatcher.OnClientDisconnected(ClientId);
|
||||
|
||||
|
||||
_packageReceiverTask = null;
|
||||
}
|
||||
|
||||
@@ -265,7 +257,7 @@ namespace MQTTnet.Server
|
||||
var retainedMessages = await _retainedMessagesManager.GetSubscribedMessagesAsync(topicFilters).ConfigureAwait(false);
|
||||
foreach (var applicationMessage in retainedMessages)
|
||||
{
|
||||
EnqueueApplicationMessage(applicationMessage, ClientId, true);
|
||||
Session.EnqueueApplicationMessage(applicationMessage, ClientId, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,15 +323,8 @@ namespace MQTTnet.Server
|
||||
var applicationMessage = _dataConverter.CreateApplicationMessage(publishPacket);
|
||||
_sessionsManager.DispatchApplicationMessage(applicationMessage, this);
|
||||
|
||||
// TODO: Create ACK packet.
|
||||
|
||||
var response = new MqttPubAckPacket
|
||||
{
|
||||
PacketIdentifier = publishPacket.PacketIdentifier,
|
||||
ReasonCode = MqttPubAckReasonCode.Success
|
||||
};
|
||||
|
||||
return SendAsync(response);
|
||||
var pubAckPacket = _dataConverter.CreatePubAckPacket(publishPacket);
|
||||
return SendAsync(pubAckPacket);
|
||||
}
|
||||
|
||||
private Task HandleIncomingPublishPacketWithQoS2Async(MqttPublishPacket publishPacket)
|
||||
@@ -354,29 +339,11 @@ namespace MQTTnet.Server
|
||||
};
|
||||
|
||||
return SendAsync(pubRecPacket);
|
||||
|
||||
////Task.Run(async () =>
|
||||
////{
|
||||
//// using (var pubRelPacketAwaiter = _packetDispatcher.AddPacketAwaiter<MqttPubRelPacket>(publishPacket.PacketIdentifier))
|
||||
//// {
|
||||
//// await pubRelPacketAwaiter.WaitOneAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
||||
|
||||
//// var pubCompPacket = new MqttPubCompPacket
|
||||
//// {
|
||||
//// PacketIdentifier = publishPacket.PacketIdentifier,
|
||||
//// ReasonCode = MqttPubCompReasonCode.Success
|
||||
//// };
|
||||
|
||||
//// await SendAsync(pubCompPacket).ConfigureAwait(false);
|
||||
//// }
|
||||
////});
|
||||
|
||||
//await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task SendPendingPacketsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
MqttPendingApplicationMessage queuedApplicationMessage = null;
|
||||
MqttQueuedApplicationMessage queuedApplicationMessage = null;
|
||||
MqttPublishPacket publishPacket = null;
|
||||
|
||||
try
|
||||
@@ -501,12 +468,12 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAdapterReadingPacketCompleted(object sender, EventArgs e)
|
||||
private void OnAdapterReadingPacketCompleted()
|
||||
{
|
||||
_keepAliveMonitor?.Resume();
|
||||
}
|
||||
|
||||
private void OnAdapterReadingPacketStarted(object sender, EventArgs e)
|
||||
private void OnAdapterReadingPacketStarted()
|
||||
{
|
||||
_keepAliveMonitor?.Pause();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Server.Status;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public class MqttClientSession
|
||||
{
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
|
||||
private readonly DateTime _createdTimestamp = DateTime.UtcNow;
|
||||
|
||||
public MqttClientSession(string clientId, MqttServerEventDispatcher eventDispatcher, IMqttServerOptions serverOptions)
|
||||
public MqttClientSession(string clientId, MqttServerEventDispatcher eventDispatcher, IMqttServerOptions serverOptions, IMqttNetChildLogger logger)
|
||||
{
|
||||
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
|
||||
|
||||
SubscriptionsManager = new MqttClientSubscriptionsManager(clientId, eventDispatcher, serverOptions);
|
||||
ApplicationMessagesQueue = new MqttClientSessionApplicationMessagesQueue(serverOptions);
|
||||
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.CreateChildLogger(nameof(MqttClientSession));
|
||||
}
|
||||
|
||||
public string ClientId { get; }
|
||||
@@ -35,12 +41,15 @@ namespace MQTTnet.Server
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Verbose("Queued application message with topic '{0}' (ClientId: {1}).", applicationMessage.Topic, ClientId);
|
||||
|
||||
ApplicationMessagesQueue.Enqueue(applicationMessage, senderClientId, checkSubscriptionsResult.QualityOfServiceLevel, isRetainedApplicationMessage);
|
||||
}
|
||||
|
||||
public async Task SubscribeAsync(ICollection<TopicFilter> topicFilters, MqttRetainedMessagesManager retainedMessagesManager)
|
||||
{
|
||||
await SubscriptionsManager.SubscribeAsync(topicFilters).ConfigureAwait(false);
|
||||
|
||||
var matchingRetainedMessages = await retainedMessagesManager.GetSubscribedMessagesAsync(topicFilters).ConfigureAwait(false);
|
||||
foreach (var matchingRetainedMessage in matchingRetainedMessages)
|
||||
{
|
||||
|
||||
@@ -1,54 +1,29 @@
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public class MqttPendingApplicationMessage
|
||||
{
|
||||
public MqttApplicationMessage ApplicationMessage { get; set; }
|
||||
|
||||
public string SenderClientId { get; set; }
|
||||
|
||||
public bool IsRetainedMessage { get; set; }
|
||||
|
||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||
|
||||
public bool IsDuplicate { get; set; }
|
||||
}
|
||||
|
||||
public class MqttClientSessionApplicationMessagesQueue : IDisposable
|
||||
{
|
||||
private readonly Queue<MqttPendingApplicationMessage> _messageQueue = new Queue<MqttPendingApplicationMessage>();
|
||||
private readonly AsyncAutoResetEvent _messageQueueLock = new AsyncAutoResetEvent();
|
||||
|
||||
private readonly AsyncQueue<MqttQueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttQueuedApplicationMessage>();
|
||||
|
||||
private readonly IMqttServerOptions _options;
|
||||
|
||||
public MqttClientSessionApplicationMessagesQueue(IMqttServerOptions options)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_messageQueue)
|
||||
{
|
||||
return _messageQueue.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
public int Count => _messageQueue.Count;
|
||||
|
||||
public void Enqueue(MqttApplicationMessage applicationMessage, string senderClientId, MqttQualityOfServiceLevel qualityOfServiceLevel, bool isRetainedMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
Enqueue(new MqttPendingApplicationMessage
|
||||
Enqueue(new MqttQueuedApplicationMessage
|
||||
{
|
||||
ApplicationMessage = applicationMessage,
|
||||
SenderClientId = senderClientId,
|
||||
@@ -59,39 +34,23 @@ namespace MQTTnet.Server
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_messageQueue)
|
||||
{
|
||||
_messageQueue.Clear();
|
||||
}
|
||||
_messageQueue.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public async Task<MqttQueuedApplicationMessage> TakeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<MqttPendingApplicationMessage> TakeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// TODO: Create a blocking queue from this.
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
var dequeueResult = await _messageQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (!dequeueResult.IsSuccess)
|
||||
{
|
||||
lock (_messageQueue)
|
||||
{
|
||||
if (_messageQueue.Count > 0)
|
||||
{
|
||||
return _messageQueue.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
await _messageQueueLock.WaitOneAsync(cancellationToken).ConfigureAwait(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return dequeueResult.Item;
|
||||
}
|
||||
|
||||
public void Enqueue(MqttPendingApplicationMessage enqueuedApplicationMessage)
|
||||
public void Enqueue(MqttQueuedApplicationMessage queuedApplicationMessage)
|
||||
{
|
||||
if (enqueuedApplicationMessage == null) throw new ArgumentNullException(nameof(enqueuedApplicationMessage));
|
||||
if (queuedApplicationMessage == null) throw new ArgumentNullException(nameof(queuedApplicationMessage));
|
||||
|
||||
lock (_messageQueue)
|
||||
{
|
||||
@@ -104,14 +63,17 @@ namespace MQTTnet.Server
|
||||
|
||||
if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
|
||||
{
|
||||
_messageQueue.Dequeue();
|
||||
_messageQueue.TryDequeue();
|
||||
}
|
||||
}
|
||||
|
||||
_messageQueue.Enqueue(enqueuedApplicationMessage);
|
||||
_messageQueue.Enqueue(queuedApplicationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
_messageQueueLock.Set();
|
||||
public void Dispose()
|
||||
{
|
||||
_messageQueue.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,9 +57,9 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
public Task HandleConnectionAsync(IMqttChannelAdapter clientAdapter)
|
||||
public Task HandleClientAsync(IMqttChannelAdapter clientAdapter)
|
||||
{
|
||||
return HandleConnectionAsync(clientAdapter, _cancellationToken);
|
||||
return HandleClientAsync(clientAdapter, _cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IList<IMqttClientStatus>> GetClientStatusAsync()
|
||||
@@ -68,7 +68,7 @@ namespace MQTTnet.Server
|
||||
|
||||
foreach (var connection in _connections.Values)
|
||||
{
|
||||
var clientStatus = new MqttClientStatus(connection, this);
|
||||
var clientStatus = new MqttClientStatus(connection);
|
||||
connection.FillStatus(clientStatus);
|
||||
|
||||
var sessionStatus = new MqttSessionStatus(connection.Session, this);
|
||||
@@ -175,7 +175,8 @@ namespace MQTTnet.Server
|
||||
return;
|
||||
}
|
||||
|
||||
var queuedApplicationMessage = await _messageQueue.DequeueAsync(cancellationToken).ConfigureAwait(false);
|
||||
var dequeueResult = await _messageQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);
|
||||
var queuedApplicationMessage = dequeueResult.Item;
|
||||
|
||||
var sender = queuedApplicationMessage.Sender;
|
||||
var applicationMessage = queuedApplicationMessage.ApplicationMessage;
|
||||
@@ -185,7 +186,10 @@ namespace MQTTnet.Server
|
||||
{
|
||||
if (interceptorContext.CloseConnection)
|
||||
{
|
||||
await queuedApplicationMessage.Sender.StopAsync().ConfigureAwait(false);
|
||||
if (sender != null)
|
||||
{
|
||||
await sender.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (interceptorContext.ApplicationMessage == null || !interceptorContext.AcceptPublish)
|
||||
@@ -206,7 +210,7 @@ namespace MQTTnet.Server
|
||||
foreach (var clientSession in _sessions.Values)
|
||||
{
|
||||
clientSession.EnqueueApplicationMessage(
|
||||
queuedApplicationMessage.ApplicationMessage,
|
||||
applicationMessage,
|
||||
sender?.ClientId,
|
||||
false);
|
||||
}
|
||||
@@ -220,7 +224,7 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
||||
private async Task HandleClientAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
||||
{
|
||||
var disconnectType = MqttClientDisconnectType.NotClean;
|
||||
var clientId = string.Empty;
|
||||
@@ -273,17 +277,9 @@ namespace MQTTnet.Server
|
||||
{
|
||||
_connections.TryRemove(clientId, out _);
|
||||
|
||||
////connection?.ReferenceCounter.Decrement();
|
||||
////if (connection?.ReferenceCounter.HasReferences == true)
|
||||
////{
|
||||
//// disconnectType = MqttClientDisconnectType.Takeover;
|
||||
////}
|
||||
////else
|
||||
if (!_options.EnablePersistentSessions)
|
||||
{
|
||||
if (!_options.EnablePersistentSessions)
|
||||
{
|
||||
await DeleteSessionAsync(clientId).ConfigureAwait(false);
|
||||
}
|
||||
await DeleteSessionAsync(clientId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await TryCleanupChannelAsync(channelAdapter).ConfigureAwait(false);
|
||||
@@ -331,8 +327,6 @@ namespace MQTTnet.Server
|
||||
{
|
||||
if (connectPacket.CleanSession)
|
||||
{
|
||||
// TODO: Check if required.
|
||||
//session.Dispose();
|
||||
session = null;
|
||||
|
||||
_logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId);
|
||||
@@ -345,7 +339,7 @@ namespace MQTTnet.Server
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
session = new MqttClientSession(connectPacket.ClientId, _eventDispatcher, _options);
|
||||
session = new MqttClientSession(connectPacket.ClientId, _eventDispatcher, _options, _logger);
|
||||
_logger.Verbose("Created a new session for client '{0}'.", connectPacket.ClientId);
|
||||
}
|
||||
|
||||
@@ -370,7 +364,13 @@ namespace MQTTnet.Server
|
||||
return null;
|
||||
}
|
||||
|
||||
var interceptorContext = new MqttApplicationMessageInterceptorContext(sender?.ClientId, applicationMessage);
|
||||
var senderClientId = sender?.ClientId;
|
||||
if (sender == null)
|
||||
{
|
||||
senderClientId = _options.ClientId;
|
||||
}
|
||||
|
||||
var interceptorContext = new MqttApplicationMessageInterceptorContext(senderClientId, applicationMessage);
|
||||
await interceptor.InterceptApplicationMessagePublishAsync(interceptorContext).ConfigureAwait(false);
|
||||
return interceptorContext;
|
||||
}
|
||||
@@ -385,10 +385,6 @@ namespace MQTTnet.Server
|
||||
{
|
||||
_logger.Error(exception, "Error while disconnecting client channel.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
channelAdapter.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public class MqttQueuedApplicationMessage
|
||||
{
|
||||
public MqttApplicationMessage ApplicationMessage { get; set; }
|
||||
|
||||
public string SenderClientId { get; set; }
|
||||
|
||||
public bool IsRetainedMessage { get; set; }
|
||||
|
||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||
|
||||
public bool IsDuplicate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -61,8 +61,9 @@ namespace MQTTnet.Server
|
||||
using (await _messagesLock.WaitAsync().ConfigureAwait(false))
|
||||
{
|
||||
var saveIsRequired = false;
|
||||
var hasPayload = applicationMessage.Payload != null && applicationMessage.Payload.Length > 0;
|
||||
|
||||
if (applicationMessage.Payload?.Length == 0)
|
||||
if (!hasPayload)
|
||||
{
|
||||
saveIsRequired = _messages.Remove(applicationMessage.Topic);
|
||||
_logger.Verbose("Client '{0}' cleared retained message for topic '{1}'.", clientId, applicationMessage.Topic);
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace MQTTnet.Server
|
||||
return _clientSessionsManager.GetSessionStatusAsync();
|
||||
}
|
||||
|
||||
public Task<IList<MqttApplicationMessage>> GetRetainedMessagesAsync()
|
||||
public Task<IList<MqttApplicationMessage>> GetRetainedApplicationMessagesAsync()
|
||||
{
|
||||
return _retainedMessagesManager.GetMessagesAsync();
|
||||
}
|
||||
@@ -127,7 +127,7 @@ namespace MQTTnet.Server
|
||||
|
||||
foreach (var adapter in _adapters)
|
||||
{
|
||||
adapter.ClientAcceptedHandler = OnClientAccepted;
|
||||
adapter.ClientHandler = OnHandleClient;
|
||||
await adapter.StartAsync(Options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ namespace MQTTnet.Server
|
||||
|
||||
foreach (var adapter in _adapters)
|
||||
{
|
||||
adapter.ClientAcceptedHandler = null;
|
||||
adapter.ClientHandler = null;
|
||||
await adapter.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -179,14 +179,14 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
public Task ClearRetainedMessagesAsync()
|
||||
public Task ClearRetainedApplicationMessagesAsync()
|
||||
{
|
||||
return _retainedMessagesManager?.ClearMessagesAsync();
|
||||
}
|
||||
|
||||
private void OnClientAccepted(MqttServerAdapterClientAcceptedEventArgs eventArgs)
|
||||
private Task OnHandleClient(IMqttChannelAdapter channelAdapter)
|
||||
{
|
||||
eventArgs.SessionTask = _clientSessionsManager.HandleConnectionAsync(eventArgs.Client);
|
||||
return _clientSessionsManager.HandleClientAsync(channelAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,110 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Client.Receiving;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public static class MqttServerExtensions
|
||||
{
|
||||
public static IMqttServer UseClientConnectedHandler(this IMqttServer server, Func<MqttServerClientConnectedEventArgs, Task> handler)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return server.UseClientConnectedHandler((IMqttServerClientConnectedHandler)null);
|
||||
}
|
||||
|
||||
return server.UseClientConnectedHandler(new MqttServerClientConnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttServer UseClientConnectedHandler(this IMqttServer server, Action<MqttServerClientConnectedEventArgs> handler)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return server.UseClientConnectedHandler((IMqttServerClientConnectedHandler)null);
|
||||
}
|
||||
|
||||
return server.UseClientConnectedHandler(new MqttServerClientConnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttServer UseClientConnectedHandler(this IMqttServer server, IMqttServerClientConnectedHandler handler)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
server.ClientConnectedHandler = handler;
|
||||
return server;
|
||||
}
|
||||
|
||||
public static IMqttServer UseClientDisconnectedHandler(this IMqttServer server, Func<MqttServerClientDisconnectedEventArgs, Task> handler)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return server.UseClientDisconnectedHandler((IMqttServerClientDisconnectedHandler)null);
|
||||
}
|
||||
|
||||
return server.UseClientDisconnectedHandler(new MqttServerClientDisconnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttServer UseClientDisconnectedHandler(this IMqttServer server, Action<MqttServerClientDisconnectedEventArgs> handler)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return server.UseClientDisconnectedHandler((IMqttServerClientDisconnectedHandler)null);
|
||||
}
|
||||
|
||||
return server.UseClientDisconnectedHandler(new MqttServerClientDisconnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttServer UseClientDisconnectedHandler(this IMqttServer server, IMqttServerClientDisconnectedHandler handler)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
server.ClientDisconnectedHandler = handler;
|
||||
return server;
|
||||
}
|
||||
|
||||
public static IMqttServer UseApplicationMessageReceivedHandler(this IMqttServer server, Func<MqttApplicationMessageReceivedEventArgs, Task> handler)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return server.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
return server.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttServer UseApplicationMessageReceivedHandler(this IMqttServer server, Action<MqttApplicationMessageReceivedEventArgs> handler)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return server.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
return server.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttServer UseApplicationMessageReceivedHandler(this IMqttServer server, IMqttApplicationMessageReceivedHandler handler)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
server.ApplicationMessageReceivedHandler = handler;
|
||||
return server;
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IMqttServer server, string clientId, params TopicFilter[] topicFilters)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
@@ -120,12 +216,16 @@ namespace MQTTnet.Server
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IMqttServer server, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder, CancellationToken cancellationToken)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
||||
return server.PublishAsync(message, cancellationToken);
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IMqttServer server, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
||||
return server.PublishAsync(message, CancellationToken.None);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace MQTTnet.Server
|
||||
|
||||
public MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; } = new MqttServerTlsTcpEndpointOptions();
|
||||
|
||||
public string ClientId { get; set; }
|
||||
|
||||
public bool EnablePersistentSessions { get; set; }
|
||||
|
||||
public int MaxPendingMessagesPerClient { get; set; } = 250;
|
||||
|
||||
@@ -147,6 +147,15 @@ namespace MQTTnet.Server
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client ID which is used when publishing messages from the server directly.
|
||||
/// </summary>
|
||||
public MqttServerOptionsBuilder WithClientId(string value)
|
||||
{
|
||||
_options.ClientId = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IMqttServerOptions Build()
|
||||
{
|
||||
return _options;
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace MQTTnet.Server
|
||||
|
||||
public int ConnectionBacklog { get; set; } = 10;
|
||||
|
||||
public bool NoDelay { get; set; } = true;
|
||||
|
||||
#if WINDOWS_UWP
|
||||
public int BufferSize { get; set; } = 4096;
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,10 @@ namespace MQTTnet.Server.Status
|
||||
|
||||
IMqttSessionStatus Session { get; }
|
||||
|
||||
long BytesSent { get; }
|
||||
|
||||
long BytesReceived { get; }
|
||||
|
||||
Task DisconnectAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ namespace MQTTnet.Server.Status
|
||||
{
|
||||
string ClientId { get; set; }
|
||||
|
||||
bool IsConnected { get; }
|
||||
|
||||
long PendingApplicationMessagesCount { get; set; }
|
||||
|
||||
Task ClearPendingApplicationMessagesAsync();
|
||||
|
||||
@@ -6,13 +6,11 @@ namespace MQTTnet.Server.Status
|
||||
{
|
||||
public class MqttClientStatus : IMqttClientStatus
|
||||
{
|
||||
private readonly MqttClientSessionsManager _sessionsManager;
|
||||
private readonly MqttClientConnection _connection;
|
||||
|
||||
public MqttClientStatus(MqttClientConnection connection, MqttClientSessionsManager sessionsManager)
|
||||
public MqttClientStatus(MqttClientConnection connection)
|
||||
{
|
||||
_connection = connection;
|
||||
_sessionsManager = sessionsManager;
|
||||
}
|
||||
|
||||
public string ClientId { get; set; }
|
||||
@@ -35,6 +33,10 @@ namespace MQTTnet.Server.Status
|
||||
|
||||
public IMqttSessionStatus Session { get; set; }
|
||||
|
||||
public long BytesSent { get; set; }
|
||||
|
||||
public long BytesReceived { get; set; }
|
||||
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
return _connection.StopAsync();
|
||||
|
||||
@@ -17,8 +17,6 @@ namespace MQTTnet.Server.Status
|
||||
public string ClientId { get; set; }
|
||||
|
||||
public long PendingApplicationMessagesCount { get; set; }
|
||||
|
||||
public bool IsConnected { get; set; }
|
||||
|
||||
public DateTime CreatedTimestamp { get; set; }
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\..\Source\MQTTnet\MQTTnet.csproj" />
|
||||
<ProjectReference Include="..\MQTTnet.Core.Tests\MQTTnet.Tests.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Tests;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class MqttPacketSerializerTestsWithSpanBasedReader : MqttPacketSerializer_Tests
|
||||
{
|
||||
protected override IMqttPacketBodyReader ReaderFactory(byte[] data)
|
||||
{
|
||||
var result = new SpanBasedMqttPacketBodyReader();
|
||||
result.SetBuffer(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override IMqttPacketWriter WriterFactory()
|
||||
{
|
||||
return new SpanBasedMqttPacketWriter();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,22 +23,25 @@ namespace MQTTnet.AspNetCore.Tests
|
||||
var consumed = part.Start;
|
||||
var observed = part.Start;
|
||||
var result = false;
|
||||
var read = 0;
|
||||
|
||||
var reader = new SpanBasedMqttPacketBodyReader();
|
||||
|
||||
part = sequence.Slice(sequence.Start, 0); // empty message should fail
|
||||
result = serializer.TryDecode(part, out packet, out consumed, out observed);
|
||||
result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
|
||||
Assert.IsFalse(result);
|
||||
|
||||
|
||||
part = sequence.Slice(sequence.Start, 1); // partial fixed header should fail
|
||||
result = serializer.TryDecode(part, out packet, out consumed, out observed);
|
||||
result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
|
||||
Assert.IsFalse(result);
|
||||
|
||||
part = sequence.Slice(sequence.Start, 4); // partial body should fail
|
||||
result = serializer.TryDecode(part, out packet, out consumed, out observed);
|
||||
result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
|
||||
Assert.IsFalse(result);
|
||||
|
||||
part = sequence; // complete msg should work
|
||||
result = serializer.TryDecode(part, out packet, out consumed, out observed);
|
||||
result = serializer.TryDecode(reader, part, out packet, out consumed, out observed, out read);
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -3,20 +3,17 @@ using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using BenchmarkDotNet.Validators;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
namespace MQTTnet.Benchmarks.Configurations
|
||||
{
|
||||
/// <summary>
|
||||
/// this options may be used to run benchmarks in debugmode and attach a performance profiler
|
||||
/// https://benchmarkdotnet.org/Configs/Configs.htm
|
||||
/// </summary>
|
||||
public class AllowNonOptimized : ManualConfig
|
||||
public class AllowNonOptimized : BaseConfig
|
||||
{
|
||||
public AllowNonOptimized()
|
||||
{
|
||||
Add(JitOptimizationsValidator.DontFailOnError); // ALLOW NON-OPTIMIZED DLLS
|
||||
Add(DefaultConfig.Instance.GetLoggers().ToArray()); // manual config has no loggers by default
|
||||
Add(DefaultConfig.Instance.GetExporters().ToArray()); // manual config has no exporters by default
|
||||
Add(DefaultConfig.Instance.GetColumnProviders().ToArray()); // manual config has no columns by default
|
||||
Add(Job.InProcess);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user