Compare commits

...
65 Commits
Author SHA1 Message Date
Christian Kratky ac88f0a05a Merge branch 'develop' 2019-05-11 09:47:31 +02:00
Christian Kratky b02f30844c Add new options for _MqttClientOptionsBuilder_. 2019-05-10 21:55:46 +02:00
Christian Kratky 1a8bcae47c Add authentication result to disconnected handler. 2019-05-09 22:08:56 +02:00
Christian Kratky 664549ab93 Fix message encoding bugs. 2019-05-08 00:12:27 +02:00
Christian 914d2b641f Update appveyor.yml 2019-05-06 21:14:51 +02:00
Christian 0cc0fcf5d9 Update appveyor.yml 2019-05-06 21:07:46 +02:00
Christian Kratky 5980cbd944 Fix build scripts to use 4.6 binaries. 2019-05-06 20:27:33 +02:00
Christian Kratky 3ef7f60a18 Fix wrong IsSessionPresent flag. 2019-05-06 20:27:24 +02:00
Christian Kratky efef23e3f5 Merge branch 'develop' 2019-05-06 20:24:12 +02:00
Christian Kratky 53ab2ac140 Add Wiki Code. 2019-05-06 20:22:41 +02:00
Christian Kratky 2ecbd83d06 Add UnitTests. 2019-05-06 20:22:20 +02:00
Christian Kratky a278b6189b Refactor empty payload detection for retained messages. 2019-05-01 19:41:34 +02:00
Christian Kratky cdbdfdec6d Add UnitTests. 2019-05-01 19:39:10 +02:00
Christian Kratky 808c2da5f6 Add PacketIdentifier for QoS 2. 2019-04-28 23:03:54 +02:00
Christian Kratky 6ed3b0cedd Add ClientId for application messages published by the server. 2019-04-24 21:37:29 +02:00
Christian Kratky 9da3377aa7 Fix credential generation. 2019-04-24 20:19:55 +02:00
Christian Kratky 0cac2aa1bc Fix broken log message. 2019-04-24 20:19:46 +02:00
Christian Kratky 2d59b5c1ac Refactoring. 2019-04-23 14:49:19 +02:00
Christian Kratky 9dfa0b32e4 Add extension methods for server. 2019-04-23 14:49:12 +02:00
Christian Kratky 20a7579156 Fix issues in ApplicationMessageInterceptor. 2019-04-23 14:48:51 +02:00
Christian Kratky c7409c2ff4 Fix missing endpoint in client status. 2019-04-23 14:48:38 +02:00
Christian Kratky d4c6fb44c9 Expose PacketIdentifier in PublishResult. 2019-04-23 14:47:42 +02:00
Christian Kratky e6cf23aebd Add API controllers for MQTTnet.Server. 2019-04-23 14:46:13 +02:00
Christian Kratky 0fec860b25 Add UnitTests. 2019-04-23 14:46:02 +02:00
Christian Kratky 2c927c5995 Cleanup. 2019-04-22 10:59:39 +02:00
Christian Kratky 6399172582 Improve connection disposal. 2019-04-22 10:50:47 +02:00
Christian Kratky 5c903e6fbf Improve connection handling. 2019-04-20 21:12:30 +02:00
Christian Kratky f60bbf7dd7 MQTTnet.Server startup cleanup. 2019-04-20 20:59:28 +02:00
Christian Kratky 519bb05aac Fix broken code. 2019-04-18 00:09:19 +02:00
Christian Kratky a8434d1b58 Expose results of publish operations for MQTTv5. 2019-04-18 00:01:22 +02:00
Christian Kratky 420b35e37d Merge remote-tracking branch 'origin/develop' into develop 2019-04-17 21:44:12 +02:00
Christian Kratky a25c8cd4c7 Fix some minor TODOs. 2019-04-17 21:44:09 +02:00
Christian 8ac77558af Merge pull request #593 from JanEggers/fixTodo
fixed todo in MqttConnectionContext
2019-04-17 19:14:00 +02:00
JanEggers 953578eba2 fixed todo in MqttConnectionContext 2019-04-17 09:47:17 +02:00
Christian 527713ccf8 Merge pull request #592 from JanEggers/spanV2
performance improvements span based serializers
2019-04-16 19:28:38 +02:00
JanEggers 477e2d23ab added benchmark option to compare netfx and core runtime 2019-04-15 15:52:51 +02:00
JanEggers 03c61ad82c fixed tests and added testcase for user property serialization 2019-04-15 15:37:15 +02:00
JanEggers 9475ccd0d8 revert accidential change 2019-04-15 15:31:31 +02:00
JanEggers 73b051d159 reuse reader 2019-04-15 15:00:09 +02:00
JanEggers a9944def68 added span based writer 2019-04-15 14:43:49 +02:00
JanEggers aee10ba6fa added netcore 2.1 build 2019-04-15 14:24:27 +02:00
JanEggers cfc1b27950 fixed build and added implementations for new read functions 2019-04-15 14:14:32 +02:00
JanEggers 149d01e173 added tests 2019-04-15 14:09:22 +02:00
JanEggers 888e1797d7 fixed cherry pick 2019-04-15 14:04:21 +02:00
JanEggers f8ade2daf4 slight improvements 2019-04-15 14:04:06 +02:00
JanEggers a85792c392 fixed another issue 2019-04-15 14:00:22 +02:00
JanEggers fbca8a6dc1 span based reader 2019-04-15 14:00:00 +02:00
Christian Kratky 732ea84caa Update build script. 2019-04-13 00:09:10 +02:00
Christian Kratky 65896269da Improve protocol version detection. 2019-04-11 20:13:30 +02:00
Christian Kratky 631f660d5c Add MQTTnet.Server to build script. 2019-04-11 20:13:08 +02:00
Christian Kratky 7f08371532 Update tests. 2019-04-08 22:29:52 +02:00
Christian Kratky 1afd59325f Add validations for client options builder. 2019-04-08 22:29:46 +02:00
Christian Kratky 45c033f401 Improve payload handling and allocation. 2019-04-08 22:29:29 +02:00
Christian Kratky 7855eccb78 Update solution files. 2019-04-07 20:54:16 +02:00
Christian Kratky d51300d32d Update build process. 2019-04-07 20:45:16 +02:00
Christian Kratky 61b10a8b56 Add extension methods. 2019-04-07 19:58:01 +02:00
Christian Kratky dbee941dcc Update nuget packages. 2019-04-04 22:00:40 +02:00
Christian Kratky fd7c98d0d1 Update docs. 2019-04-03 23:46:32 +02:00
Christian Kratky c911797f1b Fix broken config in MQTTnet.Server. 2019-04-03 23:46:26 +02:00
Christian Kratky b5a1d3a69f Add more config values for TCP connections. 2019-04-03 23:46:13 +02:00
Christian Kratky cdeb073bb0 Fix disposal of SSL streams. 2019-04-03 23:45:53 +02:00
Christian Kratky a8db0dd3d6 Rename tests. 2019-04-03 23:44:48 +02:00
Christian Kratky 2267770a72 Make internal message of server async. 2019-04-02 21:51:26 +02:00
Christian Kratky 35ee732045 Remove obsolete files from MQTTnet.Server. 2019-04-02 21:50:49 +02:00
Christian Kratky ad5834dd1f Add long runnings tests for all QoS levels. 2019-04-02 21:50:16 +02:00
140 changed files with 2913 additions and 1365 deletions
-2
View File
@@ -288,8 +288,6 @@ __pycache__/
*.odx.cs
*.xsd.cs
Build/nuget.exe
*.js
*.map
+5 -1
View File
@@ -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\"/>
@@ -31,10 +34,9 @@
<!-- .NET Framework -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net461\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net462\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net470\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net471\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net472\"/>
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files>
</package>
+8 -6
View File
@@ -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\"/>
@@ -31,10 +34,9 @@
<!-- .NET Framework -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net462\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net470\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net471\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net472\"/>
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files>
</package>
+8 -2
View File
@@ -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>
+16 -37
View File
@@ -5,37 +5,18 @@
<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).
* [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).
* [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!).
* [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.
* [ManagedClient] The internal MQTT client is now closed properly (thanks to @vbBerni).
* [Server] Added support for MQTTv5 clients. The server will still return _success_ for all cases at the moment even if more granular codes are available.
* [Server] Fixed issues in QoS 2 handling which leads to message loss.
* [Server] Replaced all events with proper async compatible handlers (BREAKING CHANGE!).
* [Server] The used logger instance is now propagated to the WebSocket server adapter.
* [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.
* [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.
<releaseNotes>
* [Core] Fixed missing properties from PUBLISH packet in _MqttApplicationMessage_ (thanks to @pcbing).
* [Core] Fixed wrong encoding of PUBREL and PUBCOMP packets for MQTTv5 (thanks to @perphilipp).
* [Client] Added the authentication result to the disconnected handler (only set when connecting failed).
* [Client] Added new overloads for _MqttClientOptionsBuilder_.
* [Server] Fixed a bug which returns wrong flag for existing session in CONNACK packet (thanks to @avengerstark).
* [nuget] .NET Framework builds are now using 4.5.2 or 4.6.1 builds instead of netstandard 2.0.
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2019</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
@@ -53,16 +34,15 @@
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.9" />
</group>
<group targetFramework="net452">
</group>
<group targetFramework="net461">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.8" />
</group>
</dependencies>
</metadata>
<files>
<!-- License -->
<file src="..\LICENSE" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
@@ -75,10 +55,9 @@
<!-- .NET Framework -->
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net461\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net462\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net470\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net471\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net472\"/>
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet\bin\Release\net461\MQTTnet.*" target="lib\net461\"/>
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files>
</package>
+53 -44
View File
@@ -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
BIN
View File
Binary file not shown.
+3 -2
View File
@@ -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
+1 -1
View File
@@ -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);
}
}
}
+32 -13
View File
@@ -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)
+3 -18
View File
@@ -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);
+20 -16
View File
@@ -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
+42 -1
View File
@@ -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()
+5 -5
View File
@@ -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": {
-12
View File
@@ -1,12 +0,0 @@
{
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"target": "es5"
},
"exclude": [
"node_modules"
]
}
-35
View File
@@ -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>
-58
View File
@@ -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);
+1 -1
View File
@@ -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();
+40 -21
View File
@@ -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; }
}
}
+3 -3
View File
@@ -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; }
}
@@ -1,17 +1,21 @@
using System;
using MQTTnet.Client.Connecting;
namespace MQTTnet.Client.Disconnecting
{
public class MqttClientDisconnectedEventArgs : EventArgs
{
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception)
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientAuthenticateResult authenticateResult)
{
ClientWasConnected = clientWasConnected;
Exception = exception;
AuthenticateResult = authenticateResult;
}
public bool ClientWasConnected { get; }
public Exception Exception { get; }
public MqttClientAuthenticateResult AuthenticateResult { get; }
}
}
+12 -10
View File
@@ -61,6 +61,8 @@ namespace MQTTnet.Client
ThrowIfConnected("It is not allowed to connect with a server after the connection is established.");
MqttClientAuthenticateResult authenticateResult = null;
try
{
Options = options;
@@ -81,7 +83,7 @@ namespace MQTTnet.Client
_packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken);
var authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, cancellationToken).ConfigureAwait(false);
authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, cancellationToken).ConfigureAwait(false);
_sendTracker.Restart();
@@ -108,7 +110,7 @@ namespace MQTTnet.Client
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(null, exception).ConfigureAwait(false);
await DisconnectInternalAsync(null, exception, authenticateResult).ConfigureAwait(false);
}
throw;
@@ -131,7 +133,7 @@ namespace MQTTnet.Client
{
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(null, null).ConfigureAwait(false);
await DisconnectInternalAsync(null, null, null).ConfigureAwait(false);
}
}
}
@@ -232,7 +234,7 @@ namespace MQTTnet.Client
if (IsConnected) throw new MqttProtocolViolationException(message);
}
private async Task DisconnectInternalAsync(Task sender, Exception exception)
private async Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult)
{
var clientWasConnected = IsConnected;
@@ -267,7 +269,7 @@ namespace MQTTnet.Client
var disconnectedHandler = DisconnectedHandler;
if (disconnectedHandler != null)
{
await disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception)).ConfigureAwait(false);
await disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, authenticateResult)).ConfigureAwait(false);
}
}
}
@@ -325,7 +327,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;
}
}
@@ -376,7 +378,7 @@ namespace MQTTnet.Client
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_keepAlivePacketsSenderTask, exception).ConfigureAwait(false);
await DisconnectInternalAsync(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
}
}
finally
@@ -404,7 +406,7 @@ namespace MQTTnet.Client
{
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, null).ConfigureAwait(false);
await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
}
return;
@@ -436,7 +438,7 @@ namespace MQTTnet.Client
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
}
finally
@@ -497,7 +499,7 @@ namespace MQTTnet.Client
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
}
}
+40 -25
View File
@@ -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;
}
@@ -131,6 +136,16 @@ namespace MQTTnet.Client.Options
return this;
}
public MqttClientOptionsBuilder WithTcpServer(Action<MqttClientTcpOptions> optionsBuilder)
{
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
_tcpOptions = new MqttClientTcpOptions();
optionsBuilder.Invoke(_tcpOptions);
return this;
}
public MqttClientOptionsBuilder WithProxy(string address, string username = null, string password = null, string domain = null, bool bypassOnLocal = false, string[] bypassList = null)
{
_proxyOptions = new MqttClientWebSocketProxyOptions
@@ -146,6 +161,15 @@ namespace MQTTnet.Client.Options
return this;
}
public MqttClientOptionsBuilder WithProxy(Action<MqttClientWebSocketProxyOptions> optionsBuilder)
{
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
_proxyOptions = new MqttClientWebSocketProxyOptions();
optionsBuilder(_proxyOptions);
return this;
}
public MqttClientOptionsBuilder WithWebSocketServer(string uri, MqttClientOptionsBuilderWebSocketParameters parameters = null)
{
_webSocketOptions = new MqttClientWebSocketOptions
@@ -158,9 +182,19 @@ namespace MQTTnet.Client.Options
return this;
}
public MqttClientOptionsBuilder WithWebSocketServer(Action<MqttClientWebSocketOptions> optionsBuilder)
{
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
_webSocketOptions = new MqttClientWebSocketOptions();
optionsBuilder.Invoke(_webSocketOptions);
return this;
}
public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters)
{
_tlsParameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
_tlsParameters = parameters;
return this;
}
@@ -169,6 +203,15 @@ namespace MQTTnet.Client.Options
return WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = true });
}
public MqttClientOptionsBuilder WithTls(Action<MqttClientOptionsBuilderTlsParameters> optionsBuilder)
{
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
_tlsParameters = new MqttClientOptionsBuilderTlsParameters();
optionsBuilder(_tlsParameters);
return this;
}
public IMqttClientOptions Build()
{
if (_tcpOptions == null && _webSocketOptions == null)
@@ -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();
+24 -46
View File
@@ -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
}
}
+57 -21
View File
@@ -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
@@ -54,6 +63,13 @@ namespace MQTTnet.Formatter.V5
Payload = publishPacket.Payload,
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
Retain = publishPacket.Retain,
ResponseTopic = publishPacket.Properties?.ResponseTopic,
ContentType = publishPacket.Properties?.ContentType,
CorrelationData = publishPacket.Properties?.CorrelationData,
MessageExpiryInterval = publishPacket.Properties?.MessageExpiryInterval,
SubscriptionIdentifier = publishPacket.Properties?.SubscriptionIdentifier,
TopicAlias = publishPacket.Properties?.TopicAlias,
PayloadFormatIndicator = publishPacket.Properties?.PayloadFormatIndicator,
UserProperties = publishPacket.Properties?.UserProperties ?? new List<MqttUserProperty>()
};
}
@@ -180,13 +196,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 +225,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);
@@ -331,9 +343,6 @@ namespace MQTTnet.Formatter.V5
ThrowReasonCodeNotSetException();
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write((byte)packet.ReasonCode.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
@@ -341,7 +350,9 @@ namespace MQTTnet.Formatter.V5
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRelReasonCode.Success)
packetWriter.Write(packet.PacketIdentifier.Value);
if (propertiesWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRelReasonCode.Success)
{
packetWriter.Write((byte)packet.ReasonCode.Value);
propertiesWriter.WriteToPacket(packetWriter);
@@ -350,7 +361,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);
@@ -360,8 +371,7 @@ namespace MQTTnet.Formatter.V5
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write((byte)packet.ReasonCode.Value);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
@@ -369,7 +379,7 @@ namespace MQTTnet.Formatter.V5
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubCompReasonCode.Success)
if (propertiesWriter.Length > 0 || packet.ReasonCode.Value != MqttPubCompReasonCode.Success)
{
packetWriter.Write((byte)packet.ReasonCode.Value);
propertiesWriter.WriteToPacket(packetWriter);
@@ -378,9 +388,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 +435,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 +460,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 +484,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 +509,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 +542,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()
@@ -7,8 +7,11 @@ namespace MQTTnet.Formatter.V5
{
public class MqttV500PropertiesWriter
{
// TODO: Consider lazy init on first write to avoid useless allocations.
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
public int Length => _packetWriter.Length;
public void WriteUserProperties(List<MqttUserProperty> userProperties)
{
if (userProperties == null || userProperties.Count == 0)
@@ -63,7 +66,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;
+24 -4
View File
@@ -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; }
}
}
+2 -6
View File
@@ -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>
+1 -1
View File
@@ -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;
}
}
+1 -5
View File
@@ -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, "]");
}
}
}
+2 -2
View File
@@ -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);
+3 -1
View File
@@ -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; }
}
}
+29 -62
View File
@@ -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;
@@ -150,7 +138,7 @@ namespace MQTTnet.Server
{
ReturnCode = MqttConnectReturnCode.ConnectionAccepted,
ReasonCode = MqttConnectReasonCode.Success,
IsSessionPresent = Session.IsCleanSession
IsSessionPresent = !Session.IsCleanSession
}).ConfigureAwait(false);
Session.IsCleanSession = false;
@@ -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
{
+10 -1
View File
@@ -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);
+6 -6
View File
@@ -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);
}
}
}
+101 -1
View File
@@ -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);
}
}

Some files were not shown because too many files have changed in this diff Show More