mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 00:15:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c22b6033e3 | ||
|
|
27476a9194 | ||
|
|
12973a4179 | ||
|
|
01b437dc08 | ||
|
|
ac88f0a05a | ||
|
|
b02f30844c | ||
|
|
1a8bcae47c | ||
|
|
664549ab93 | ||
|
|
914d2b641f | ||
|
|
0cc0fcf5d9 | ||
|
|
5980cbd944 | ||
|
|
3ef7f60a18 | ||
|
|
efef23e3f5 | ||
|
|
53ab2ac140 | ||
|
|
2ecbd83d06 | ||
|
|
a278b6189b | ||
|
|
cdbdfdec6d | ||
|
|
808c2da5f6 | ||
|
|
6ed3b0cedd | ||
|
|
9da3377aa7 | ||
|
|
0cac2aa1bc | ||
|
|
2d59b5c1ac | ||
|
|
9dfa0b32e4 | ||
|
|
20a7579156 | ||
|
|
c7409c2ff4 | ||
|
|
d4c6fb44c9 | ||
|
|
e6cf23aebd | ||
|
|
0fec860b25 | ||
|
|
2c927c5995 | ||
|
|
6399172582 | ||
|
|
5c903e6fbf | ||
|
|
f60bbf7dd7 | ||
|
|
519bb05aac | ||
|
|
a8434d1b58 | ||
|
|
420b35e37d | ||
|
|
a25c8cd4c7 | ||
|
|
8ac77558af | ||
|
|
953578eba2 | ||
|
|
527713ccf8 | ||
|
|
477e2d23ab | ||
|
|
03c61ad82c | ||
|
|
9475ccd0d8 | ||
|
|
73b051d159 | ||
|
|
a9944def68 | ||
|
|
aee10ba6fa | ||
|
|
cfc1b27950 | ||
|
|
149d01e173 | ||
|
|
888e1797d7 | ||
|
|
f8ade2daf4 | ||
|
|
a85792c392 | ||
|
|
fbca8a6dc1 | ||
|
|
732ea84caa | ||
|
|
65896269da | ||
|
|
631f660d5c | ||
|
|
7f08371532 | ||
|
|
1afd59325f | ||
|
|
45c033f401 | ||
|
|
7855eccb78 | ||
|
|
d51300d32d | ||
|
|
61b10a8b56 | ||
|
|
dbee941dcc | ||
|
|
fd7c98d0d1 | ||
|
|
c911797f1b | ||
|
|
b5a1d3a69f | ||
|
|
cdeb073bb0 | ||
|
|
a8db0dd3d6 | ||
|
|
2267770a72 | ||
|
|
35ee732045 | ||
|
|
ad5834dd1f | ||
|
|
a7204234f6 | ||
|
|
e8c33077b7 | ||
|
|
bd86dae8a9 | ||
|
|
7188aaf79d | ||
|
|
bafcc81672 | ||
|
|
d0f1659eec | ||
|
|
11a5b5a080 | ||
|
|
7ed5580352 | ||
|
|
efc6f124b4 | ||
|
|
7c3d88f62d | ||
|
|
b383a54416 | ||
|
|
673fc5e20c | ||
|
|
f2b9642712 | ||
|
|
4f03f22afb | ||
|
|
27a4af6070 | ||
|
|
0184ce400c | ||
|
|
653ea8a102 | ||
|
|
3450889e84 | ||
|
|
1311535eed | ||
|
|
e1799ea6a3 | ||
|
|
cd76f434e3 | ||
|
|
fd91170aee | ||
|
|
cd1bfc3a10 | ||
|
|
f074e9ed04 | ||
|
|
562a5304f6 | ||
|
|
1e697a94a1 | ||
|
|
0e2c3abd05 | ||
|
|
7321774c45 | ||
|
|
4a65cab769 | ||
|
|
510c3bd0bf | ||
|
|
0c056b511e | ||
|
|
2ca6866c5e | ||
|
|
ebcce9b8be | ||
|
|
9e7c70e649 | ||
|
|
bef0860e53 | ||
|
|
67c9177731 | ||
|
|
007991f073 | ||
|
|
532dddeb6c | ||
|
|
9070087fad | ||
|
|
de8bcb7f37 | ||
|
|
5090cfb411 | ||
|
|
6e64d653c5 | ||
|
|
461bc6e594 | ||
|
|
0d95dc563c | ||
|
|
03bfc5f00b | ||
|
|
8708609498 | ||
|
|
dffc27c1f4 | ||
|
|
6f5f507323 | ||
|
|
26d6d817cd | ||
|
|
b68247f56d | ||
|
|
3b02a5f6aa | ||
|
|
09718a00e7 | ||
|
|
ecc2cd5f0c | ||
|
|
618580dc15 | ||
|
|
4860cec6c7 | ||
|
|
b9e0dbf150 | ||
|
|
2a4eb460da | ||
|
|
087c622122 | ||
|
|
2000de15e7 | ||
|
|
5e6b902430 | ||
|
|
a1f414a06d | ||
|
|
de3a39bdc2 | ||
|
|
6c5c9e5bf2 | ||
|
|
506873ed5b | ||
|
|
6a2bded181 | ||
|
|
ebf0e91ab8 | ||
|
|
107e96ff7d | ||
|
|
0d6e244f84 | ||
|
|
96a67579e2 | ||
|
|
4cba814716 | ||
|
|
b527059b72 | ||
|
|
c0507fcc55 | ||
|
|
38f2453c92 | ||
|
|
058bb65d31 | ||
|
|
38b215e9b5 | ||
|
|
45df33c9a5 | ||
|
|
89becaa191 | ||
|
|
6c87e4e039 | ||
|
|
cbe06d6b87 | ||
|
|
4fed80c2d0 | ||
|
|
74e4fd8b3f | ||
|
|
cc113a6c34 | ||
|
|
fca9876111 | ||
|
|
ac9f6a36f8 | ||
|
|
929b60e044 | ||
|
|
acb433dd4c | ||
|
|
c4e8bf7f39 | ||
|
|
ba4fdfb51c | ||
|
|
dfda916283 | ||
|
|
40326df9a9 | ||
|
|
a94444098a | ||
|
|
0c13e7a504 | ||
|
|
0dba57fe75 | ||
|
|
6483f896c1 | ||
|
|
8fa9943c3d | ||
|
|
195357835b | ||
|
|
28b2562112 | ||
|
|
ab24368de1 | ||
|
|
9b51404b4d | ||
|
|
619eaf284d | ||
|
|
86c348df58 | ||
|
|
fdb2ec897c | ||
|
|
b091fee554 | ||
|
|
14ec6ee793 | ||
|
|
0cfef0649a | ||
|
|
7292af7db4 | ||
|
|
bc1a197c4f | ||
|
|
ccc275902f | ||
|
|
0f5fb8aa3a | ||
|
|
83eca982e9 | ||
|
|
017f17a6b1 | ||
|
|
b3564e01a4 | ||
|
|
43105f71d8 | ||
|
|
40c408a9c0 | ||
|
|
7baff7c080 | ||
|
|
91a918d30b | ||
|
|
2a93fc18e4 | ||
|
|
31de6ee186 | ||
|
|
74d59673a8 | ||
|
|
d1c3d9b453 | ||
|
|
baa9241216 | ||
|
|
2c6c3ac6a4 | ||
|
|
b2c31331b6 | ||
|
|
9ff39c1fd9 | ||
|
|
210d57b254 | ||
|
|
7292cedd2f | ||
|
|
48d5afa799 | ||
|
|
3db22eea51 | ||
|
|
2fbb44ec66 | ||
|
|
d7b98080f5 | ||
|
|
3e7b41e712 | ||
|
|
5f18f35e02 | ||
|
|
af39e87703 | ||
|
|
ace25af0e8 | ||
|
|
8d78455414 | ||
|
|
fed16d7710 | ||
|
|
fcff3a2c8d | ||
|
|
803dd6f64e | ||
|
|
a1e258eb24 | ||
|
|
e9df698a1f | ||
|
|
bddcbf6999 | ||
|
|
be22e2551d | ||
|
|
4e01c7f13d | ||
|
|
7181813f12 | ||
|
|
6fc9f12c34 | ||
|
|
cc4e37fa40 | ||
|
|
ccadd4e41a | ||
|
|
a5caab62f8 | ||
|
|
2bff218806 | ||
|
|
95c30a2843 | ||
|
|
28efbcdb3d | ||
|
|
ca7952ab33 | ||
|
|
62693af196 | ||
|
|
5abee2512f | ||
|
|
c9a6155834 | ||
|
|
aad3ab903f | ||
|
|
4c76a898b6 | ||
|
|
d273148bc5 | ||
|
|
5314f5c67a | ||
|
|
22aab44d49 | ||
|
|
b819746165 | ||
|
|
73d33e2200 | ||
|
|
740bc3aa30 | ||
|
|
583b32f17a | ||
|
|
b7ab29473b | ||
|
|
ffc4f1408a | ||
|
|
c8b3066883 | ||
|
|
fb4f89b412 | ||
|
|
6bee6a0469 | ||
|
|
0c2ab9b231 | ||
|
|
03bef4c15d | ||
|
|
ba62ca7306 | ||
|
|
56a1530cee | ||
|
|
699cd5fc23 | ||
|
|
381c59d1e5 | ||
|
|
70f4dc9e10 | ||
|
|
0367ad31f1 | ||
|
|
5c6bcdbca6 | ||
|
|
400c0f05ad | ||
|
|
00df7c9b7b | ||
|
|
a272ed7daa | ||
|
|
b2e7a2e370 | ||
|
|
39bc70cbf0 | ||
|
|
682298b69f | ||
|
|
028a39ec25 | ||
|
|
3b104049b4 | ||
|
|
dd83a360b4 | ||
|
|
976c62d9c2 | ||
|
|
4cc9f12a03 | ||
|
|
0b5a751c56 | ||
|
|
5a59cd278c | ||
|
|
cdb05e5894 | ||
|
|
6c08b5caf3 | ||
|
|
d5b53f3712 | ||
|
|
80fbb2f0b2 | ||
|
|
b4f90decc7 | ||
|
|
61b324043a | ||
|
|
2ced921008 | ||
|
|
1e2fee0f33 | ||
|
|
ac36c9c406 | ||
|
|
f70c79aaf9 | ||
|
|
0839fa5757 | ||
|
|
60bc4ebcee | ||
|
|
65e239652d | ||
|
|
6b9015a928 | ||
|
|
a740c38ee6 | ||
|
|
603639126e | ||
|
|
043cfdb3f9 | ||
|
|
b0da4b2b2d | ||
|
|
5402859a30 | ||
|
|
2b10200101 | ||
|
|
55e8927ab7 | ||
|
|
d30fe61264 | ||
|
|
f7ea2d29e1 | ||
|
|
e6cfef5295 | ||
|
|
0dea9e6a2b | ||
|
|
ae80de26ec | ||
|
|
53bd2a0960 | ||
|
|
b55f5ff298 | ||
|
|
21048b51ae | ||
|
|
773fd09166 | ||
|
|
57532a290a | ||
|
|
740a35b018 | ||
|
|
6e995230a7 | ||
|
|
65a8b091b9 | ||
|
|
fa34908d82 | ||
|
|
b09eb1213c |
+1
-2
@@ -235,6 +235,7 @@ FakesAssemblies/
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
/*/**/package-lock.json
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
@@ -287,8 +288,6 @@ __pycache__/
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
|
||||
Build/nuget.exe
|
||||
*.js
|
||||
*.map
|
||||
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
language: csharp
|
||||
solution: MQTTnet.sln
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- dotnet: 2.0.0
|
||||
mono: none
|
||||
dist: trusty
|
||||
env: DOTNETCORE=1 # optional, can be used to take different code paths in your script
|
||||
- mono: latest
|
||||
@@ -5,25 +5,28 @@
|
||||
<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>This is a support library to integrate MQTTnet into AspNetCore.</description>
|
||||
<releaseNotes>* Updated to MQTTnet 2.8.1.
|
||||
* For more release notes please check the MQTTnet release notes.
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</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>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.1" />
|
||||
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.0" />
|
||||
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.0.1" />
|
||||
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.0.1" />
|
||||
<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,22 +5,23 @@
|
||||
<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>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
|
||||
<releaseNotes>* Updated to MQTTnet 2.8.1.
|
||||
* For more release notes please check the MQTTnet release notes.
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</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>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.1" />
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
</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\"/>
|
||||
|
||||
@@ -33,7 +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\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>
|
||||
</package>
|
||||
|
||||
@@ -5,22 +5,23 @@
|
||||
<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>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
||||
<releaseNotes>* Updated to MQTTnet 2.8.1.
|
||||
* For more release notes please check the MQTTnet release notes.
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</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>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.1" />
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
</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\"/>
|
||||
|
||||
@@ -33,7 +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\"/>
|
||||
<!-- .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>
|
||||
</package>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet.NETStandard</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<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). This package contains the .NET Standard version of the library only.</description>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</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>
|
||||
<dependencies>
|
||||
<group targetFramework="netstandard1.3">
|
||||
<dependency id="NETStandard.Library" version="1.3.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.0">
|
||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- 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>
|
||||
+13
-16
@@ -5,26 +5,22 @@
|
||||
<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] Performance optimizations.
|
||||
* [Core] Fixed a bug which prevents receiving large packets (UWP only)
|
||||
* [Client] The ManagedClient options now allow configuring the interval for connection checks.
|
||||
* [Server] Added the Endpoint of the Adapter (remote IP and port) to the connection validation callback.
|
||||
* [Server] The ipv4 and ipv6 endpoint can be disabled now by setting the bound IP address to _None_.
|
||||
* [Server] Fix a bug in the keep alive monitor which caused high CPU load (thanks to @GarageGadget).
|
||||
<releaseNotes>
|
||||
* [Core] Fixed issues in MQTTv5 message encoding and decoding.
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<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>
|
||||
<dependencies>
|
||||
<group targetFramework="netstandard1.3">
|
||||
<dependency id="NETStandard.Library" version="1.3.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.0">
|
||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||
@@ -33,16 +29,15 @@
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="uap10.0">
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.4" />
|
||||
</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\"/>
|
||||
|
||||
@@ -55,7 +50,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\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
-32
@@ -3,45 +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)
|
||||
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\nuget.exe pack MQTTnet.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
|
||||
}
|
||||
# Create NuGet packages.
|
||||
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
Copy-Item MQTTnet.AspNetCore.nuspec -Destination MQTTnet.AspNetCore.nuspec.old -Force
|
||||
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
|
||||
Copy-Item MQTTnet.Extensions.Rpc.nuspec -Destination MQTTnet.Extensions.Rpc.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.Rpc.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.Rpc.nuspec
|
||||
Copy-Item MQTTnet.Extensions.ManagedClient.nuspec -Destination MQTTnet.Extensions.ManagedClient.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.ManagedClient.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.ManagedClient.nuspec
|
||||
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.NETStandard.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
|
||||
Move-Item MQTTnet.AspNetCore.nuspec.old -Destination MQTTnet.AspNetCore.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.Rpc.nuspec.old -Destination MQTTnet.Extensions.Rpc.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.ManagedClient.nuspec.old -Destination MQTTnet.Extensions.ManagedClient.nuspec -Force
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Christian Kratky
|
||||
MQTTnet Copyright (c) 2016-2019 Christian Kratky
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Build\build.ps1 = Build\build.ps1
|
||||
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.nuspec = Build\MQTTnet.nuspec
|
||||
Build\upload.ps1 = Build\upload.ps1
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.bettercodehub.yml = .bettercodehub.yml
|
||||
appveyor.yml = appveyor.yml
|
||||
LICENSE = LICENSE
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.NetCore", "Tests\MQTTnet.TestApp.NetCore\MQTTnet.TestApp.NetCore.csproj", "{3D283AAD-AAA8-4339-8394-52F80B6304DB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.AspNetCore2", "Tests\MQTTnet.TestApp.AspNetCore2\MQTTnet.TestApp.AspNetCore2.csproj", "{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{12816BCC-AF9E-44A9-9AE5-C246AF2A0587}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Rpc", "Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj", "{C444E9C8-95FA-430E-9126-274129DE16CD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Tests\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{998D04DD-7CB0-45F5-A393-E2495C16399E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedClient", "Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj", "{C400533A-8EBA-4F0B-BF4D-295C3708604B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x86.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+23
-3
@@ -1,9 +1,9 @@
|
||||
|
||||
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.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
|
||||
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
|
||||
@@ -47,6 +48,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedC
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -226,6 +229,22 @@ Global
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -241,6 +260,7 @@ Global
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<p align="center">
|
||||
<img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/Logo_128x128.png?raw=true" width="128">
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
[](https://www.nuget.org/packages/MQTTnet)
|
||||
[](https://ci.appveyor.com/project/chkr1011/mqttnet/branch/develop)
|
||||
[](https://ci.appveyor.com/project/chkr1011/mqttnet)
|
||||
[](https://bettercodehub.com/)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
@@ -18,12 +20,14 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
|
||||
* Async support
|
||||
* TLS 1.2 support for client and server (but not UWP servers)
|
||||
* Extensible communication channels (i.e. In-Memory, TCP, TCP+TLS, WS)
|
||||
* Extensible communication channels (e.g. In-Memory, TCP, TCP+TLS, WS)
|
||||
* Lightweight (only the low level implementation of MQTT, no overhead)
|
||||
* Performance optimized (processing ~70.000 messages / second)*
|
||||
* Uniform API across all supported versions of the MQTT protocol
|
||||
* Interfaces included for mocking and testing
|
||||
* Access to internal trace messages
|
||||
* Unit tested (~90 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_.
|
||||
|
||||
@@ -46,6 +50,17 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
* WebSockets supported (via ASP.NET Core 2.0, separate nuget)
|
||||
* A custom message interceptor can be added which allows transforming or extending every received application message
|
||||
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
||||
* Connect clients with different protocol versions at the same time.
|
||||
|
||||
## MQTTnet Server
|
||||
|
||||
_MQTTnet Server_ is a standalone cross platform MQTT server (like mosquitto) basing on this library. It has the following features.
|
||||
* Running portable (no installation required)
|
||||
* Runs und Windows, Linux, macOS, Raspberry Pi
|
||||
* Python scripting support for manipulating messages, validation of clients, building business logic etc.
|
||||
* Supports WebSocket and TCP (with and without TLS) connections
|
||||
* Provides a HTTP based API (including Swagger endpoint)
|
||||
* Extensive configuration parameters and customization supported
|
||||
|
||||
## Supported frameworks
|
||||
|
||||
@@ -60,7 +75,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
|
||||
## Supported MQTT versions
|
||||
|
||||
* 5.0.0 (planned)
|
||||
* 5.0.0
|
||||
* 3.1.1
|
||||
* 3.1.0
|
||||
|
||||
@@ -77,21 +92,25 @@ Please find examples and the documentation at the Wiki of this repository (<http
|
||||
If you want to contribute to this project just create a pull request. But only pull requests which are matching the code style of this library will be accepted. Before creating a pull request please have a look at the library to get an overview of the required style.
|
||||
Also additions and updates in the Wiki are welcome.
|
||||
|
||||
This project also listed at Open Collective (https://opencollective.com/mqttnet).
|
||||
**This project, like all projects requires time and money. If you feel compelled to support the development team, you can do so through Open Collective (https://opencollective.com/mqttnet)**.
|
||||
|
||||
## References
|
||||
|
||||
This library is used in the following projects:
|
||||
|
||||
* Azure Functions MQTT Bindings, [CaseOnline.Azure.WebJobs.Extensions.Mqtt](https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt/)
|
||||
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
|
||||
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
|
||||
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
|
||||
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
|
||||
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
|
||||
|
||||
If you use this library and want to see your project here please let me know.
|
||||
If you use this library and want to see your project here please create a pull request.
|
||||
|
||||
## MIT License
|
||||
## License
|
||||
|
||||
Copyright (c) 2017-2018 Christian Kratky
|
||||
MIT License
|
||||
|
||||
MQTTnet Copyright (c) 2016-2019 Christian Kratky
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -28,10 +28,9 @@ 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))
|
||||
{
|
||||
var endpoint = $"{context.Connection.RemoteIpAddress}:{context.Connection.RemotePort}";
|
||||
await adapter.RunWebSocketConnectionAsync(webSocket, endpoint);
|
||||
await adapter.RunWebSocketConnectionAsync(webSocket, context);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
|
||||
{
|
||||
public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,9 @@ using System.Net;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore.Client.Tcp;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client
|
||||
{
|
||||
@@ -14,15 +15,16 @@ namespace MQTTnet.AspNetCore.Client
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
|
||||
|
||||
switch (options.ChannelOptions)
|
||||
{
|
||||
case MqttClientTcpOptions tcpOptions:
|
||||
{
|
||||
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
|
||||
var tcpConnection = new TcpConnection(endpoint);
|
||||
return new MqttConnectionContext(serializer, 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'">
|
||||
@@ -14,9 +15,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.1" />
|
||||
<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>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
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 MQTTnet.Serializer;
|
||||
using System;
|
||||
using System.IO.Pipelines;
|
||||
using System.Threading;
|
||||
@@ -12,33 +14,68 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class MqttConnectionContext : IMqttChannelAdapter
|
||||
{
|
||||
public MqttConnectionContext(
|
||||
IMqttPacketSerializer packetSerializer,
|
||||
ConnectionContext connection)
|
||||
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
|
||||
{
|
||||
PacketSerializer = packetSerializer;
|
||||
Connection = connection;
|
||||
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 ConnectionContext Connection { get; }
|
||||
public IMqttPacketSerializer PacketSerializer { get; }
|
||||
public event EventHandler ReadingPacketStarted;
|
||||
public event EventHandler ReadingPacketCompleted;
|
||||
private PipeReader _input;
|
||||
private PipeWriter _output;
|
||||
private readonly SpanBasedMqttPacketBodyReader _reader;
|
||||
|
||||
public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
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 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 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;
|
||||
}
|
||||
@@ -59,7 +96,7 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
else
|
||||
{
|
||||
readResult = await readTask;
|
||||
readResult = await readTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var buffer = readResult.Buffer;
|
||||
@@ -71,19 +108,20 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
if (PacketSerializer.TryDeserialize(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)
|
||||
{
|
||||
break;
|
||||
throw new MqttCommunicationException("Connection Aborted");
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -97,17 +135,34 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReadingPacketCompleted?.Invoke(this, EventArgs.Empty);
|
||||
ReadingPacketCompletedCallback?.Invoke();
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return null;
|
||||
}
|
||||
|
||||
public Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
var buffer = PacketSerializer.Serialize(packet);
|
||||
return Connection.Transport.Output.WriteAsync(buffer.AsMemory(), cancellationToken).AsTask();
|
||||
var formatter = PacketFormatterAdapter;
|
||||
|
||||
|
||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
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
|
||||
{
|
||||
_writerSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
|
||||
{
|
||||
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||
{
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||
{
|
||||
@@ -21,14 +21,15 @@ namespace MQTTnet.AspNetCore
|
||||
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
|
||||
}
|
||||
|
||||
|
||||
var serializer = new MqttPacketSerializer();
|
||||
using (var adapter = new MqttConnectionContext(serializer, connection))
|
||||
var writer = new SpanBasedMqttPacketWriter();
|
||||
var formatter = new MqttPacketFormatterAdapter(writer);
|
||||
using (var adapter = new MqttConnectionContext(formatter, connection))
|
||||
{
|
||||
var args = new MqttServerAdapterClientAcceptedEventArgs(adapter);
|
||||
ClientAccepted?.Invoke(this, args);
|
||||
|
||||
await args.SessionTask;
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
await clientHandler(adapter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +42,7 @@ namespace MQTTnet.AspNetCore
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class MqttWebSocketServerAdapter : IMqttServerAdapter
|
||||
{
|
||||
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
|
||||
public MqttWebSocketServerAdapter(IMqttNetChildLogger logger)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_logger = logger.CreateChildLogger(nameof(MqttTcpServerAdapter));
|
||||
}
|
||||
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public Task StartAsync(IMqttServerOptions options)
|
||||
{
|
||||
@@ -23,24 +33,31 @@ namespace MQTTnet.AspNetCore
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, string endpoint)
|
||||
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
||||
{
|
||||
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
|
||||
|
||||
var clientAdapter = new MqttChannelAdapter(new MqttWebSocketChannel(webSocket, endpoint), new MqttPacketSerializer(), new MqttNetLogger().CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
|
||||
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
|
||||
|
||||
var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
|
||||
var isSecureConnection = clientCertificate != null;
|
||||
clientCertificate?.Dispose();
|
||||
|
||||
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
|
||||
ClientAccepted?.Invoke(this, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,26 +2,36 @@
|
||||
using System.Buffers;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Serializer;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public static class ReaderExtensions
|
||||
{
|
||||
public static bool TryDeserialize(this IMqttPacketSerializer serializer, 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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -32,34 +42,45 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
|
||||
var bodySlice = copy.Slice(0, bodyLength);
|
||||
var buffer = bodySlice.GetArray();
|
||||
packet = serializer.Deserialize(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length)));
|
||||
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
|
||||
{
|
||||
@@ -67,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;
|
||||
@@ -81,7 +102,8 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
input = input.Slice(index);
|
||||
|
||||
result = value;
|
||||
headerLength = index;
|
||||
bodyLength = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,27 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure)
|
||||
{
|
||||
var builder = new MqttServerOptionsBuilder();
|
||||
services.AddSingleton<IMqttServerOptions>(s => {
|
||||
var builder = new MqttServerOptionsBuilder();
|
||||
configure(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
configure(builder);
|
||||
|
||||
services.AddSingleton<IMqttServerOptions>(builder.Build());
|
||||
services.AddHostedMqttServer();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
|
||||
{
|
||||
services.AddSingleton<IMqttServerOptions>(s => {
|
||||
var builder = new AspNetMqttServerOptionsBuilder(s);
|
||||
configure(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
services.AddHostedMqttServer();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ApplicationMessageProcessedHandlerDelegate : IApplicationMessageProcessedHandler
|
||||
{
|
||||
private readonly Func<ApplicationMessageProcessedEventArgs, Task> _handler;
|
||||
|
||||
public ApplicationMessageProcessedHandlerDelegate(Action<ApplicationMessageProcessedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = context =>
|
||||
{
|
||||
handler(context);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
|
||||
public ApplicationMessageProcessedHandlerDelegate(Func<ApplicationMessageProcessedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleApplicationMessageProcessedAsync(ApplicationMessageProcessedEventArgs eventArgs)
|
||||
{
|
||||
return _handler(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ApplicationMessageSkippedEventArgs : EventArgs
|
||||
{
|
||||
public ApplicationMessageSkippedEventArgs(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||
}
|
||||
|
||||
public ManagedMqttApplicationMessage ApplicationMessage { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ApplicationMessageSkippedHandlerDelegate : IApplicationMessageSkippedHandler
|
||||
{
|
||||
private readonly Func<ApplicationMessageSkippedEventArgs, Task> _handler;
|
||||
|
||||
public ApplicationMessageSkippedHandlerDelegate(Action<ApplicationMessageSkippedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = eventArgs =>
|
||||
{
|
||||
handler(eventArgs);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
|
||||
public ApplicationMessageSkippedHandlerDelegate(Func<ApplicationMessageSkippedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleApplicationMessageSkippedAsync(ApplicationMessageSkippedEventArgs eventArgs)
|
||||
{
|
||||
return _handler(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ConnectingFailedHandlerDelegate : IConnectingFailedHandler
|
||||
{
|
||||
private readonly Func<ManagedProcessFailedEventArgs, Task> _handler;
|
||||
|
||||
public ConnectingFailedHandlerDelegate(Action<ManagedProcessFailedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = eventArgs =>
|
||||
{
|
||||
handler(eventArgs);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
|
||||
public ConnectingFailedHandlerDelegate(Func<ManagedProcessFailedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleConnectingFailedAsync(ManagedProcessFailedEventArgs eventArgs)
|
||||
{
|
||||
return _handler(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public interface IApplicationMessageProcessedHandler
|
||||
{
|
||||
Task HandleApplicationMessageProcessedAsync(ApplicationMessageProcessedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public interface IApplicationMessageSkippedHandler
|
||||
{
|
||||
Task HandleApplicationMessageSkippedAsync(ApplicationMessageSkippedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public interface IConnectingFailedHandler
|
||||
{
|
||||
Task HandleConnectingFailedAsync(ManagedProcessFailedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -10,14 +11,16 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
bool IsStarted { get; }
|
||||
bool IsConnected { get; }
|
||||
int PendingApplicationMessagesCount { get; }
|
||||
IManagedMqttClientOptions Options { get; }
|
||||
|
||||
event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||
IMqttClientConnectedHandler ConnectedHandler { get; set; }
|
||||
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
|
||||
|
||||
event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
|
||||
IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
|
||||
IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
|
||||
|
||||
event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
|
||||
event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
|
||||
IConnectingFailedHandler ConnectingFailedHandler { get; set; }
|
||||
ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
|
||||
|
||||
Task StartAsync(IManagedMqttClientOptions options);
|
||||
Task StopAsync();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -12,5 +13,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
TimeSpan ConnectionCheckInterval { get; }
|
||||
|
||||
IManagedMqttClientStorage Storage { get; }
|
||||
|
||||
int MaxPendingMessages { get; }
|
||||
|
||||
MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public interface ISynchronizingSubscriptionsFailedHandler
|
||||
{
|
||||
Task HandleSynchronizingSubscriptionsFailedAsync(ManagedProcessFailedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Client.Receiving;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ManagedMqttClient : IManagedMqttClient
|
||||
{
|
||||
private readonly BlockingCollection<ManagedMqttApplicationMessage> _messageQueue = new BlockingCollection<ManagedMqttApplicationMessage>();
|
||||
private readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>();
|
||||
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
||||
private readonly HashSet<string> _unsubscriptions = new HashSet<string>();
|
||||
|
||||
@@ -22,40 +27,56 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
private CancellationTokenSource _connectionCancellationToken;
|
||||
private CancellationTokenSource _publishingCancellationToken;
|
||||
private Task _maintainConnectionTask;
|
||||
|
||||
private ManagedMqttClientStorageManager _storageManager;
|
||||
private IManagedMqttClientOptions _options;
|
||||
|
||||
private bool _disposed = false;
|
||||
private bool _subscriptionsNotPushed;
|
||||
|
||||
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetChildLogger logger)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
|
||||
_mqttClient.Connected += OnConnected;
|
||||
_mqttClient.Disconnected += OnDisconnected;
|
||||
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
||||
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.CreateChildLogger(nameof(ManagedMqttClient));
|
||||
}
|
||||
|
||||
public bool IsConnected => _mqttClient.IsConnected;
|
||||
public bool IsStarted => _connectionCancellationToken != null;
|
||||
public int PendingApplicationMessagesCount => _messageQueue.Count;
|
||||
public IManagedMqttClientOptions Options { get; private set; }
|
||||
|
||||
public event EventHandler<MqttClientConnectedEventArgs> Connected;
|
||||
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
||||
public IMqttClientConnectedHandler ConnectedHandler
|
||||
{
|
||||
get => _mqttClient.ConnectedHandler;
|
||||
set => _mqttClient.ConnectedHandler = value;
|
||||
}
|
||||
|
||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||
public event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
|
||||
public IMqttClientDisconnectedHandler DisconnectedHandler
|
||||
{
|
||||
get => _mqttClient.DisconnectedHandler;
|
||||
set => _mqttClient.DisconnectedHandler = value;
|
||||
}
|
||||
|
||||
public event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
|
||||
public event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
|
||||
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler
|
||||
{
|
||||
get => _mqttClient.ApplicationMessageReceivedHandler;
|
||||
set => _mqttClient.ApplicationMessageReceivedHandler = value;
|
||||
}
|
||||
|
||||
public IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
|
||||
|
||||
public IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
|
||||
|
||||
public IConnectingFailedHandler ConnectingFailedHandler { get; set; }
|
||||
|
||||
public ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
|
||||
|
||||
public async Task StartAsync(IManagedMqttClientOptions options)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
|
||||
|
||||
@@ -64,64 +85,119 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
throw new NotSupportedException("The managed client does not support existing sessions.");
|
||||
}
|
||||
|
||||
if (_connectionCancellationToken != null) throw new InvalidOperationException("The managed client is already started.");
|
||||
if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
|
||||
|
||||
_options = options;
|
||||
Options = options;
|
||||
|
||||
if (_options.Storage != null)
|
||||
if (Options.Storage != null)
|
||||
{
|
||||
_storageManager = new ManagedMqttClientStorageManager(_options.Storage);
|
||||
_storageManager = new ManagedMqttClientStorageManager(Options.Storage);
|
||||
var messages = await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
|
||||
|
||||
foreach (var message in messages)
|
||||
{
|
||||
_messageQueue.Add(message);
|
||||
_messageQueue.Enqueue(message);
|
||||
}
|
||||
}
|
||||
|
||||
_connectionCancellationToken = new CancellationTokenSource();
|
||||
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
Task.Run(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token);
|
||||
_maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token);
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
|
||||
_logger.Info("Started");
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
public async Task StopAsync()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
StopPublishing();
|
||||
StopMaintainingConnection();
|
||||
|
||||
while (_messageQueue.Any())
|
||||
{
|
||||
_messageQueue.Take();
|
||||
}
|
||||
_messageQueue.Clear();
|
||||
|
||||
return Task.FromResult(0);
|
||||
if (_maintainConnectionTask != null)
|
||||
{
|
||||
await Task.WhenAny(_maintainConnectionTask);
|
||||
_maintainConnectionTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Task PublishAsync(MqttApplicationMessage applicationMessage)
|
||||
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
return PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build());
|
||||
await PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build()).ConfigureAwait(false);
|
||||
return new MqttClientPublishResult();
|
||||
}
|
||||
|
||||
public async Task PublishAsync(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage.Topic);
|
||||
|
||||
ManagedMqttApplicationMessage removedMessage = null;
|
||||
ApplicationMessageSkippedEventArgs applicationMessageSkippedEventArgs = null;
|
||||
|
||||
try
|
||||
{
|
||||
lock (_messageQueue)
|
||||
{
|
||||
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
||||
{
|
||||
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
|
||||
{
|
||||
_logger.Verbose("Skipping publish of new application message because internal queue is full.");
|
||||
applicationMessageSkippedEventArgs = new ApplicationMessageSkippedEventArgs(applicationMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
|
||||
{
|
||||
removedMessage = _messageQueue.RemoveFirst();
|
||||
_logger.Verbose("Removed oldest application message from internal queue because it is full.");
|
||||
applicationMessageSkippedEventArgs = new ApplicationMessageSkippedEventArgs(removedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
_messageQueue.Enqueue(applicationMessage);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (applicationMessageSkippedEventArgs != null)
|
||||
{
|
||||
var applicationMessageSkippedHandler = ApplicationMessageSkippedHandler;
|
||||
if (applicationMessageSkippedHandler != null)
|
||||
{
|
||||
await applicationMessageSkippedHandler.HandleApplicationMessageSkippedAsync(applicationMessageSkippedEventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (_storageManager != null)
|
||||
{
|
||||
if (removedMessage != null)
|
||||
{
|
||||
await _storageManager.RemoveAsync(removedMessage).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_messageQueue.Add(applicationMessage);
|
||||
}
|
||||
|
||||
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
lock (_subscriptions)
|
||||
@@ -138,6 +214,8 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public Task UnsubscribeAsync(IEnumerable<string> topics)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (topics == null) throw new ArgumentNullException(nameof(topics));
|
||||
|
||||
lock (_subscriptions)
|
||||
@@ -157,9 +235,31 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_messageQueue?.Dispose();
|
||||
_connectionCancellationToken?.Dispose();
|
||||
_publishingCancellationToken?.Dispose();
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
|
||||
StopPublishing();
|
||||
StopMaintainingConnection();
|
||||
|
||||
if (_maintainConnectionTask != null)
|
||||
{
|
||||
Task.WaitAny(_maintainConnectionTask);
|
||||
_maintainConnectionTask = null;
|
||||
}
|
||||
|
||||
_mqttClient.Dispose();
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(ManagedMqttClient));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
|
||||
@@ -180,8 +280,19 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
|
||||
_logger.Info("Stopped");
|
||||
if (!_disposed)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while disconnecting.");
|
||||
}
|
||||
|
||||
_logger.Info("Stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +304,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
if (connectionState == ReconnectionResult.NotConnected)
|
||||
{
|
||||
StopPublishing();
|
||||
await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
|
||||
await Task.Delay(Options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -206,7 +317,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
if (connectionState == ReconnectionResult.StillConnected)
|
||||
{
|
||||
await Task.Delay(_options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
|
||||
await Task.Delay(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -226,9 +337,17 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
while (!cancellationToken.IsCancellationRequested && _mqttClient.IsConnected)
|
||||
{
|
||||
var message = _messageQueue.Take(cancellationToken);
|
||||
//Peek at the message without dequeueing in order to prevent the
|
||||
//possibility of the queue growing beyond the configured cap.
|
||||
//Previously, messages could be re-enqueued if there was an
|
||||
//exception, and this re-enqueueing did not honor the cap.
|
||||
//Furthermore, because re-enqueueing would shuffle the order
|
||||
//of the messages, the DropOldestQueuedMessage strategy would
|
||||
//be unable to know which message is actually the oldest and would
|
||||
//instead drop the first item in the queue.
|
||||
var message = _messageQueue.PeekAndWait();
|
||||
if (message == null)
|
||||
{
|
||||
continue;
|
||||
@@ -258,6 +377,16 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
try
|
||||
{
|
||||
_mqttClient.PublishAsync(message.ApplicationMessage).GetAwaiter().GetResult();
|
||||
lock (_messageQueue) //lock to avoid conflict with this.PublishAsync
|
||||
{
|
||||
//While publishing this message, this.PublishAsync could have booted this
|
||||
//message off the queue to make room for another (when using a cap
|
||||
//with the DropOldestQueuedMessage strategy). If the first item
|
||||
//in the queue is equal to this message, then it's safe to remove
|
||||
//it from the queue. If not, that means this.PublishAsync has already
|
||||
//removed it, in which case we don't want to do anything.
|
||||
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
||||
}
|
||||
_storageManager?.RemoveAsync(message).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
@@ -266,9 +395,19 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
_logger.Warning(exception, $"Publishing application ({message.Id}) message failed.");
|
||||
|
||||
if (message.ApplicationMessage.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||
if (message.ApplicationMessage.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||
{
|
||||
_messageQueue.Add(message);
|
||||
//If QoS 0, we don't want this message to stay on the queue.
|
||||
//If QoS 1 or 2, it's possible that, when using a cap, this message
|
||||
//has been booted off the queue by this.PublishAsync, in which case this
|
||||
//thread will not continue to try to publish it. While this does
|
||||
//contradict the expected behavior of QoS 1 and 2, that's also true
|
||||
//for the usage of a message queue cap, so it's still consistent
|
||||
//with prior behavior in that way.
|
||||
lock (_messageQueue) //lock to avoid conflict with this.PublishAsync
|
||||
{
|
||||
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -278,20 +417,20 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
finally
|
||||
{
|
||||
ApplicationMessageProcessed?.Invoke(this, new ApplicationMessageProcessedEventArgs(message, transmitException));
|
||||
ApplicationMessageProcessedHandler?.HandleApplicationMessageProcessedAsync(new ApplicationMessageProcessedEventArgs(message, transmitException)).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SynchronizeSubscriptionsAsync()
|
||||
{
|
||||
_logger.Info(nameof(ManagedMqttClient), "Synchronizing subscriptions");
|
||||
_logger.Info("Synchronizing subscriptions");
|
||||
|
||||
List<TopicFilter> subscriptions;
|
||||
HashSet<string> unsubscriptions;
|
||||
|
||||
lock (_subscriptions)
|
||||
{
|
||||
subscriptions = _subscriptions.Select(i => new TopicFilter(i.Key, i.Value)).ToList();
|
||||
subscriptions = _subscriptions.Select(i => new TopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList();
|
||||
|
||||
unsubscriptions = new HashSet<string>(_unsubscriptions);
|
||||
_unsubscriptions.Clear();
|
||||
@@ -306,14 +445,14 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
try
|
||||
{
|
||||
if (subscriptions.Any())
|
||||
{
|
||||
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (unsubscriptions.Any())
|
||||
{
|
||||
await _mqttClient.UnsubscribeAsync(unsubscriptions).ConfigureAwait(false);
|
||||
await _mqttClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (subscriptions.Any())
|
||||
{
|
||||
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -321,7 +460,11 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
_logger.Warning(exception, "Synchronizing subscriptions failed.");
|
||||
_subscriptionsNotPushed = true;
|
||||
|
||||
SynchronizingSubscriptionsFailed?.Invoke(this, new MqttManagedProcessFailedEventArgs(exception));
|
||||
var synchronizingSubscriptionsFailedHandler = SynchronizingSubscriptionsFailedHandler;
|
||||
if (SynchronizingSubscriptionsFailedHandler != null)
|
||||
{
|
||||
await synchronizingSubscriptionsFailedHandler.HandleSynchronizingSubscriptionsFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,31 +477,21 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
try
|
||||
{
|
||||
await _mqttClient.ConnectAsync(_options.ClientOptions).ConfigureAwait(false);
|
||||
await _mqttClient.ConnectAsync(Options.ClientOptions).ConfigureAwait(false);
|
||||
return ReconnectionResult.Reconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ConnectingFailed?.Invoke(this, new MqttManagedProcessFailedEventArgs(exception));
|
||||
var connectingFailedHandler = ConnectingFailedHandler;
|
||||
if (connectingFailedHandler != null)
|
||||
{
|
||||
await connectingFailedHandler.HandleConnectingFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return ReconnectionResult.NotConnected;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
ApplicationMessageReceived?.Invoke(this, eventArgs);
|
||||
}
|
||||
|
||||
private void OnDisconnected(object sender, MqttClientDisconnectedEventArgs eventArgs)
|
||||
{
|
||||
Disconnected?.Invoke(this, eventArgs);
|
||||
}
|
||||
|
||||
private void OnConnected(object sender, MqttClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
Connected?.Invoke(this, eventArgs);
|
||||
}
|
||||
|
||||
|
||||
private void StartPublishing()
|
||||
{
|
||||
if (_publishingCancellationToken != null)
|
||||
@@ -367,7 +500,6 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
_publishingCancellationToken = cts;
|
||||
|
||||
Task.Factory.StartNew(() => PublishQueuedMessages(cts.Token), cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
@@ -387,4 +519,4 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
_connectionCancellationToken = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,229 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Client.Receiving;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public static class ManagedMqttClientExtensions
|
||||
{
|
||||
public static Task SubscribeAsync(this IManagedMqttClient managedClient, params TopicFilter[] topicFilters)
|
||||
public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, Func<MqttClientConnectedEventArgs, Task> handler)
|
||||
{
|
||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return managedClient.SubscribeAsync(topicFilters);
|
||||
if (handler == null)
|
||||
{
|
||||
return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
|
||||
}
|
||||
|
||||
return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IManagedMqttClient managedClient, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, Action<MqttClientConnectedEventArgs> handler)
|
||||
{
|
||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
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 client, params TopicFilter[] topicFilters)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return client.SubscribeAsync(topicFilters);
|
||||
}
|
||||
|
||||
public static Task SubscribeAsync(this IManagedMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||
|
||||
foreach (var applicationMessage in applicationMessages)
|
||||
{
|
||||
await client.PublishAsync(applicationMessage).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
return client.PublishAsync(applicationMessage, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task PublishAsync(this IManagedMqttClient client, params MqttApplicationMessage[] applicationMessages)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||
|
||||
foreach (var applicationMessage in applicationMessages)
|
||||
{
|
||||
await client.PublishAsync(applicationMessage, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(builder => builder
|
||||
.WithTopic(topic));
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(builder => builder
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload));
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(builder => builder
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel));
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(builder => builder
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
||||
.WithRetainFlag(retain));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -12,5 +14,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
public TimeSpan ConnectionCheckInterval { get; set; } = TimeSpan.FromSeconds(1);
|
||||
|
||||
public IManagedMqttClientStorage Storage { get; set; }
|
||||
|
||||
public int MaxPendingMessages { get; set; } = int.MaxValue;
|
||||
|
||||
public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropNewMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
@@ -8,6 +10,18 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
|
||||
private MqttClientOptionsBuilder _clientOptionsBuilder;
|
||||
|
||||
public ManagedMqttClientOptionsBuilder WithMaxPendingMessages(int value)
|
||||
{
|
||||
_options.MaxPendingMessages = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ManagedMqttClientOptionsBuilder WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy value)
|
||||
{
|
||||
_options.PendingMessagesOverflowStrategy = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ManagedMqttClientOptionsBuilder WithAutoReconnectDelay(TimeSpan value)
|
||||
{
|
||||
_options.AutoReconnectDelay = value;
|
||||
|
||||
@@ -28,7 +28,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public async Task AddAsync(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
_messages.Add(applicationMessage);
|
||||
await SaveAsync().ConfigureAwait(false);
|
||||
@@ -37,7 +39,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
public async Task RemoveAsync(ManagedMqttApplicationMessage applicationMessage)
|
||||
{
|
||||
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
var index = _messages.IndexOf(applicationMessage);
|
||||
if (index == -1)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ManagedProcessFailedEventArgs : EventArgs
|
||||
{
|
||||
public ManagedProcessFailedEventArgs(Exception exception)
|
||||
{
|
||||
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
public Exception Exception { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,23 @@
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public static class MqttFactoryExtensions
|
||||
{
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this IMqttClientFactory factory)
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this IMqttFactory factory)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), new MqttNetLogger().CreateChildLogger());
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger.CreateChildLogger());
|
||||
}
|
||||
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this IMqttClientFactory factory, IMqttNetLogger logger)
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this IMqttFactory factory, IMqttNetLogger logger)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), logger.CreateChildLogger());
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger.CreateChildLogger());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class MqttManagedProcessFailedEventArgs : EventArgs
|
||||
{
|
||||
public MqttManagedProcessFailedEventArgs(Exception exception)
|
||||
{
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
public Exception Exception { get; }
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class SynchronizingSubscriptionsFailedHandlerDelegate : ISynchronizingSubscriptionsFailedHandler
|
||||
{
|
||||
private readonly Func<ManagedProcessFailedEventArgs, Task> _handler;
|
||||
|
||||
public SynchronizingSubscriptionsFailedHandlerDelegate(Action<ManagedProcessFailedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = context =>
|
||||
{
|
||||
handler(context);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
|
||||
public SynchronizingSubscriptionsFailedHandlerDelegate(Func<ManagedProcessFailedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleSynchronizingSubscriptionsFailedAsync(ManagedProcessFailedEventArgs eventArgs)
|
||||
{
|
||||
return _handler(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
|
||||
@@ -13,12 +13,17 @@ namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
|
||||
private readonly IMqttClient _mqttClient;
|
||||
private readonly RpcAwareApplicationMessageReceivedHandler _applicationMessageReceivedHandler;
|
||||
|
||||
public MqttRpcClient(IMqttClient mqttClient)
|
||||
{
|
||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
|
||||
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
||||
_applicationMessageReceivedHandler = new RpcAwareApplicationMessageReceivedHandler(
|
||||
mqttClient.ApplicationMessageReceivedHandler,
|
||||
HandleApplicationMessageReceivedAsync);
|
||||
|
||||
_mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler;
|
||||
}
|
||||
|
||||
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
@@ -45,6 +50,11 @@ namespace MQTTnet.Extensions.Rpc
|
||||
throw new ArgumentException("The method name cannot contain /, + or #.");
|
||||
}
|
||||
|
||||
if (!(_mqttClient.ApplicationMessageReceivedHandler is RpcAwareApplicationMessageReceivedHandler))
|
||||
{
|
||||
throw new InvalidOperationException("The application message received handler was modified.");
|
||||
}
|
||||
|
||||
var requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{methodName}";
|
||||
var responseTopic = requestTopic + "/response";
|
||||
|
||||
@@ -102,26 +112,25 @@ namespace MQTTnet.Extensions.Rpc
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
private Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var tcs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (tcs.Task.IsCompleted || tcs.Task.IsCanceled)
|
||||
{
|
||||
return;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
tcs.TrySetResult(eventArgs.ApplicationMessage.Payload);
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler.OriginalHandler;
|
||||
|
||||
foreach (var tcs in _waitingCalls)
|
||||
{
|
||||
tcs.Value.SetCanceled();
|
||||
tcs.Value.TrySetCanceled();
|
||||
}
|
||||
|
||||
_waitingCalls.Clear();
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Receiving;
|
||||
|
||||
namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
public class RpcAwareApplicationMessageReceivedHandler : IMqttApplicationMessageReceivedHandler
|
||||
{
|
||||
private readonly Func<MqttApplicationMessageReceivedEventArgs, Task> _handleReceivedApplicationMessageAsync;
|
||||
|
||||
public RpcAwareApplicationMessageReceivedHandler(
|
||||
IMqttApplicationMessageReceivedHandler originalHandler,
|
||||
Func<MqttApplicationMessageReceivedEventArgs, Task> handleReceivedApplicationMessageAsync)
|
||||
{
|
||||
OriginalHandler = originalHandler;
|
||||
_handleReceivedApplicationMessageAsync = handleReceivedApplicationMessageAsync ?? throw new ArgumentNullException(nameof(handleReceivedApplicationMessageAsync));
|
||||
}
|
||||
|
||||
public IMqttApplicationMessageReceivedHandler OriginalHandler { get; }
|
||||
|
||||
public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
if (OriginalHandler != null)
|
||||
{
|
||||
await OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await _handleReceivedApplicationMessageAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public class RetainedApplicationMessagesModel
|
||||
{
|
||||
public bool Persist { get; set; } = false;
|
||||
|
||||
public int WriteInterval { get; set; } = 10;
|
||||
|
||||
public string Filename { get; set; } = "RetainedApplicationMessages.json";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Main Settings Model
|
||||
/// </summary>
|
||||
public class SettingsModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Set default connection timeout in seconds
|
||||
/// </summary>
|
||||
public int CommunicationTimeout { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// Set 0 to disable connection backlogging
|
||||
/// </summary>
|
||||
public int ConnectionBacklog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable support for persistent sessions
|
||||
/// </summary>
|
||||
public bool EnablePersistentSessions { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Listen Settings
|
||||
/// </summary>
|
||||
public TcpEndPointModel TcpEndPoint { get; set; } = new TcpEndPointModel();
|
||||
|
||||
/// <summary>
|
||||
/// Encryption Listen Settings
|
||||
/// </summary>
|
||||
public TcpEndPointModel EncryptedTcpEndPoint { get; set; } = new TcpEndPointModel();
|
||||
|
||||
/// <summary>
|
||||
/// Settings for the Web Socket endpoint.
|
||||
/// </summary>
|
||||
public WebSocketEndPointModel WebSocketEndPoint { get; set; } = new WebSocketEndPointModel();
|
||||
|
||||
/// <summary>
|
||||
/// Set limit for max pending messages per client
|
||||
/// </summary>
|
||||
public int MaxPendingMessagesPerClient { get; set; } = 250;
|
||||
|
||||
/// <summary>
|
||||
/// The settings for retained messages.
|
||||
/// </summary>
|
||||
public RetainedApplicationMessagesModel RetainedApplicationMessages { get; set; } = new RetainedApplicationMessagesModel();
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables the MQTTnet internal logging.
|
||||
/// </summary>
|
||||
public bool EnableDebugLogging { get; set; } = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Listen Entry Settings Model
|
||||
/// </summary>
|
||||
public class TcpEndPointModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Path to Certificate
|
||||
/// </summary>
|
||||
public string CertificatePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enabled / Disable
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Listen Address
|
||||
/// </summary>
|
||||
public string IPv4 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Listen Address
|
||||
/// </summary>
|
||||
public string IPv6 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Listen Port
|
||||
/// </summary>
|
||||
public int Port { get; set; } = 1883;
|
||||
|
||||
/// <summary>
|
||||
/// Read Certificate file
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public byte[] ReadCertificate()
|
||||
{
|
||||
if (string.IsNullOrEmpty(CertificatePath) || string.IsNullOrWhiteSpace(CertificatePath))
|
||||
{
|
||||
throw new FileNotFoundException("No path set");
|
||||
}
|
||||
|
||||
if (!File.Exists(CertificatePath))
|
||||
{
|
||||
throw new FileNotFoundException($"Could not find Certificate in path: {CertificatePath}");
|
||||
}
|
||||
|
||||
return File.ReadAllBytes(CertificatePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read IPv4
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool TryReadIPv4(out IPAddress address)
|
||||
{
|
||||
if (IPv4 == "*")
|
||||
{
|
||||
address = IPAddress.Any;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPv4 == "localhost")
|
||||
{
|
||||
address = IPAddress.Loopback;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPv4 == "disable")
|
||||
{
|
||||
address = IPAddress.None;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPAddress.TryParse(IPv4, out var ip))
|
||||
{
|
||||
address = ip;
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new System.Exception($"Could not parse IPv4 address: {IPv4}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read IPv6
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool TryReadIPv6(out IPAddress address)
|
||||
{
|
||||
if (IPv6 == "*")
|
||||
{
|
||||
address = IPAddress.IPv6Any;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPv6 == "localhost")
|
||||
{
|
||||
address = IPAddress.IPv6Loopback;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPv6 == "disable")
|
||||
{
|
||||
address = IPAddress.None;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IPAddress.TryParse(IPv6, out var ip))
|
||||
{
|
||||
address = ip;
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new System.Exception($"Could not parse IPv6 address: {IPv6}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Server.Configuration
|
||||
{
|
||||
public class WebSocketEndPointModel
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
public string Path { get; set; } = "/mqtt";
|
||||
|
||||
public int ReceiveBufferSize { get; set; } = 4096;
|
||||
|
||||
public int KeepAliveInterval { get; set; } = 120;
|
||||
|
||||
public List<string> AllowedOrigins { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Christian Kratky
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Server.Logging
|
||||
{
|
||||
public class MqttNetChildLoggerWrapper : IMqttNetChildLogger
|
||||
{
|
||||
private readonly MqttNetLoggerWrapper _logger;
|
||||
private readonly string _source;
|
||||
|
||||
public MqttNetChildLoggerWrapper(string source, MqttNetLoggerWrapper logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_source = source;
|
||||
}
|
||||
|
||||
public IMqttNetChildLogger CreateChildLogger(string source = null)
|
||||
{
|
||||
return _logger.CreateChildLogger(source);
|
||||
}
|
||||
|
||||
public void Verbose(string message, params object[] parameters)
|
||||
{
|
||||
_logger.Publish(MqttNetLogLevel.Verbose, _source, message, parameters, null);
|
||||
}
|
||||
|
||||
public void Info(string message, params object[] parameters)
|
||||
{
|
||||
_logger.Publish(MqttNetLogLevel.Info, _source, message, parameters, null);
|
||||
}
|
||||
|
||||
public void Warning(Exception exception, string message, params object[] parameters)
|
||||
{
|
||||
_logger.Publish(MqttNetLogLevel.Warning, _source, message, parameters, exception);
|
||||
}
|
||||
|
||||
public void Error(Exception exception, string message, params object[] parameters)
|
||||
{
|
||||
_logger.Publish(MqttNetLogLevel.Error, _source, message, parameters, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Server.Logging
|
||||
{
|
||||
public class MqttNetLoggerWrapper : IMqttNetLogger
|
||||
{
|
||||
private readonly ILogger<MqttServer> _logger;
|
||||
|
||||
public MqttNetLoggerWrapper(ILogger<MqttServer> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
||||
|
||||
public IMqttNetChildLogger CreateChildLogger(string source = null)
|
||||
{
|
||||
return new MqttNetChildLoggerWrapper(source, this);
|
||||
}
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
||||
{
|
||||
var convertedLogLevel = ConvertLogLevel(logLevel);
|
||||
_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)
|
||||
{
|
||||
case MqttNetLogLevel.Error: return LogLevel.Error;
|
||||
case MqttNetLogLevel.Warning: return LogLevel.Warning;
|
||||
case MqttNetLogLevel.Info: return LogLevel.Information;
|
||||
case MqttNetLogLevel.Verbose: return LogLevel.Debug;
|
||||
}
|
||||
|
||||
return LogLevel.Debug;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||
<AssemblyName>MQTTnet.Server</AssemblyName>
|
||||
<RootNamespace>MQTTnet.Server</RootNamespace>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<Company />
|
||||
<Product>MQTTnet</Product>
|
||||
<Description />
|
||||
<Authors />
|
||||
<PackageId />
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TypeScriptToolsVersion>3.3</TypeScriptToolsVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<StartupObject></StartupObject>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile></DocumentationFile>
|
||||
<WarningsAsErrors>NU1605</WarningsAsErrors>
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IronPython" Version="2.7.9" />
|
||||
<PackageReference Include="IronPython.StdLib" Version="2.7.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.0.0-rc2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.0.0-rc2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="LICENSE">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="run.sh">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="run.bat">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scripts\readme.md">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scripts\00_sample.py">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Server.Configuration;
|
||||
using MQTTnet.Server.Logging;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class CustomMqttFactory
|
||||
{
|
||||
private readonly MqttFactory _mqttFactory;
|
||||
|
||||
public CustomMqttFactory(SettingsModel settings, ILogger<MqttServer> logger)
|
||||
{
|
||||
if (settings == null) throw new ArgumentNullException(nameof(settings));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
// It is important to avoid injecting the logger wrapper to ensure that no
|
||||
// unused log messages are generated by the MQTTnet library. Debug logging
|
||||
// has a huge performance impact.
|
||||
if (settings.EnableDebugLogging)
|
||||
{
|
||||
var mqttNetLogger = new MqttNetLoggerWrapper(logger);
|
||||
_mqttFactory = new MqttFactory(mqttNetLogger);
|
||||
|
||||
logger.LogWarning("Debug logging is enabled. Performance of MQTTnet Server is decreased!");
|
||||
}
|
||||
else
|
||||
{
|
||||
_mqttFactory = new MqttFactory();
|
||||
}
|
||||
|
||||
Logger = _mqttFactory.DefaultLogger;
|
||||
|
||||
}
|
||||
|
||||
public IMqttNetLogger Logger { get; }
|
||||
|
||||
public IMqttServer CreateMqttServer(List<IMqttServerAdapter> adapters)
|
||||
{
|
||||
if (adapters == null) throw new ArgumentNullException(nameof(adapters));
|
||||
|
||||
return _mqttFactory.CreateMqttServer(adapters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttApplicationMessageInterceptor : IMqttServerApplicationMessageInterceptor
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttApplicationMessageInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttApplicationMessageInterceptor> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task InterceptApplicationMessagePublishAsync(MqttApplicationMessageInterceptorContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonContext = new PythonDictionary
|
||||
{
|
||||
{ "accept_publish", context.AcceptPublish },
|
||||
{ "close_connection", context.CloseConnection },
|
||||
{ "client_id", context.ClientId },
|
||||
{ "topic", context.ApplicationMessage.Topic },
|
||||
{ "qos", (int)context.ApplicationMessage.QualityOfServiceLevel },
|
||||
{ "retain", context.ApplicationMessage.Retain }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_application_message", pythonContext);
|
||||
|
||||
context.AcceptPublish = (bool)pythonContext.get("accept_publish", context.AcceptPublish);
|
||||
context.CloseConnection = (bool)pythonContext.get("close_connection", context.CloseConnection);
|
||||
context.ApplicationMessage.Topic = (string)pythonContext.get("topic", context.ApplicationMessage.Topic);
|
||||
context.ApplicationMessage.QualityOfServiceLevel = (MqttQualityOfServiceLevel)(int)pythonContext.get("qos", (int)context.ApplicationMessage.QualityOfServiceLevel);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while intercepting application message.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttClientConnectedHandler : IMqttServerClientConnectedHandler
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttClientConnectedHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientConnectedHandler> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task HandleClientConnectedAsync(MqttServerClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonEventArgs = new PythonDictionary
|
||||
{
|
||||
{ "client_id", eventArgs.ClientId }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_client_connected", pythonEventArgs);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while handling client connected event.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttClientDisconnectedHandler : IMqttServerClientDisconnectedHandler
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttClientDisconnectedHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientDisconnectedHandler> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task HandleClientDisconnectedAsync(MqttServerClientDisconnectedEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonEventArgs = new PythonDictionary
|
||||
{
|
||||
{ "client_id", eventArgs.ClientId },
|
||||
{ "type", PythonConvert.Pythonfy(eventArgs.DisconnectType) }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_client_disconnected", pythonEventArgs);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while handling client disconnected event.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttClientSubscribedTopicHandler : IMqttServerClientSubscribedTopicHandler
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttClientSubscribedTopicHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientSubscribedTopicHandler> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task HandleClientSubscribedTopicAsync(MqttServerClientSubscribedTopicEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonEventArgs = new PythonDictionary
|
||||
{
|
||||
{ "client_id", eventArgs.ClientId },
|
||||
{ "topic", eventArgs.TopicFilter.Topic },
|
||||
{ "qos", (int)eventArgs.TopicFilter.QualityOfServiceLevel }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_client_subscribed_topic", pythonEventArgs);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while handling client subscribed topic event.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttClientUnsubscribedTopicHandler : IMqttServerClientUnsubscribedTopicHandler
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttClientUnsubscribedTopicHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientUnsubscribedTopicHandler> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task HandleClientUnsubscribedTopicAsync(MqttServerClientUnsubscribedTopicEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonEventArgs = new PythonDictionary
|
||||
{
|
||||
{ "client_id", eventArgs.ClientId },
|
||||
{ "topic", eventArgs.TopicFilter }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_client_unsubscribed_topic", pythonEventArgs);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while handling client unsubscribed topic event.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttConnectionValidator : IMqttServerConnectionValidator
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttConnectionValidator(PythonScriptHostService pythonScriptHostService, ILogger<MqttConnectionValidator> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task ValidateConnectionAsync(MqttConnectionValidatorContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonContext = new PythonDictionary
|
||||
{
|
||||
{ "client_id", context.ClientId },
|
||||
{ "endpoint", context.Endpoint },
|
||||
{ "is_secure_connection", context.IsSecureConnection },
|
||||
{ "username", context.Username },
|
||||
{ "password", context.Password },
|
||||
{ "result", PythonConvert.Pythonfy(context.ReturnCode) }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_validate_client_connection", pythonContext);
|
||||
|
||||
context.ReturnCode = PythonConvert.ParseEnum<MqttConnectReturnCode>((string)pythonContext["result"]);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while validating client connection.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Security.Authentication;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
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
|
||||
{
|
||||
public class MqttServerService
|
||||
{
|
||||
private readonly ILogger<MqttServerService> _logger;
|
||||
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly MqttApplicationMessageInterceptor _mqttApplicationMessageInterceptor;
|
||||
private readonly MqttServerStorage _mqttServerStorage;
|
||||
private readonly MqttClientConnectedHandler _mqttClientConnectedHandler;
|
||||
private readonly MqttClientDisconnectedHandler _mqttClientDisconnectedHandler;
|
||||
private readonly MqttClientSubscribedTopicHandler _mqttClientSubscribedTopicHandler;
|
||||
private readonly MqttClientUnsubscribedTopicHandler _mqttClientUnsubscribedTopicHandler;
|
||||
private readonly MqttConnectionValidator _mqttConnectionValidator;
|
||||
private readonly IMqttServer _mqttServer;
|
||||
private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly MqttWebSocketServerAdapter _webSocketServerAdapter;
|
||||
|
||||
public MqttServerService(
|
||||
SettingsModel settings,
|
||||
CustomMqttFactory mqttFactory,
|
||||
MqttClientConnectedHandler mqttClientConnectedHandler,
|
||||
MqttClientDisconnectedHandler mqttClientDisconnectedHandler,
|
||||
MqttClientSubscribedTopicHandler mqttClientSubscribedTopicHandler,
|
||||
MqttClientUnsubscribedTopicHandler mqttClientUnsubscribedTopicHandler,
|
||||
MqttConnectionValidator mqttConnectionValidator,
|
||||
MqttSubscriptionInterceptor mqttSubscriptionInterceptor,
|
||||
MqttApplicationMessageInterceptor mqttApplicationMessageInterceptor,
|
||||
MqttServerStorage mqttServerStorage,
|
||||
PythonScriptHostService pythonScriptHostService,
|
||||
ILogger<MqttServerService> logger)
|
||||
{
|
||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||
_mqttClientConnectedHandler = mqttClientConnectedHandler ?? throw new ArgumentNullException(nameof(mqttClientConnectedHandler));
|
||||
_mqttClientDisconnectedHandler = mqttClientDisconnectedHandler ?? throw new ArgumentNullException(nameof(mqttClientDisconnectedHandler));
|
||||
_mqttClientSubscribedTopicHandler = mqttClientSubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientSubscribedTopicHandler));
|
||||
_mqttClientUnsubscribedTopicHandler = mqttClientUnsubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientUnsubscribedTopicHandler));
|
||||
_mqttConnectionValidator = mqttConnectionValidator ?? throw new ArgumentNullException(nameof(mqttConnectionValidator));
|
||||
_mqttSubscriptionInterceptor = mqttSubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttSubscriptionInterceptor));
|
||||
_mqttApplicationMessageInterceptor = mqttApplicationMessageInterceptor ?? throw new ArgumentNullException(nameof(mqttApplicationMessageInterceptor));
|
||||
_mqttServerStorage = mqttServerStorage ?? throw new ArgumentNullException(nameof(mqttServerStorage));
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_webSocketServerAdapter = new MqttWebSocketServerAdapter(mqttFactory.Logger.CreateChildLogger());
|
||||
|
||||
var adapters = new List<IMqttServerAdapter>
|
||||
{
|
||||
new MqttTcpServerAdapter(mqttFactory.Logger.CreateChildLogger()),
|
||||
_webSocketServerAdapter
|
||||
};
|
||||
|
||||
_mqttServer = mqttFactory.CreateMqttServer(adapters);
|
||||
}
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
_pythonScriptHostService.RegisterProxyObject("publish", new Action<PythonDictionary>(Publish));
|
||||
|
||||
_mqttServerStorage.Configure();
|
||||
|
||||
_mqttServer.ClientConnectedHandler = _mqttClientConnectedHandler;
|
||||
_mqttServer.ClientDisconnectedHandler = _mqttClientDisconnectedHandler;
|
||||
_mqttServer.ClientSubscribedTopicHandler = _mqttClientSubscribedTopicHandler;
|
||||
_mqttServer.ClientUnsubscribedTopicHandler = _mqttClientUnsubscribedTopicHandler;
|
||||
|
||||
_mqttServer.StartAsync(CreateMqttServerOptions()).GetAwaiter().GetResult();
|
||||
|
||||
_logger.LogInformation("MQTT server started.");
|
||||
}
|
||||
|
||||
public Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
||||
{
|
||||
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
|
||||
{
|
||||
var applicationMessageBuilder = new MqttApplicationMessageBuilder()
|
||||
.WithTopic((string)parameters.get("topic", null))
|
||||
.WithRetainFlag((bool)parameters.get("retain", false))
|
||||
.WithQualityOfServiceLevel((MqttQualityOfServiceLevel)(int)parameters.get("qos", 0));
|
||||
|
||||
var payload = parameters.get("payload", null);
|
||||
byte[] binaryPayload;
|
||||
|
||||
if (payload == null)
|
||||
{
|
||||
binaryPayload = new byte[0];
|
||||
}
|
||||
else if (payload is string stringPayload)
|
||||
{
|
||||
binaryPayload = Encoding.UTF8.GetBytes(stringPayload);
|
||||
}
|
||||
else if (payload is ByteArray byteArray)
|
||||
{
|
||||
binaryPayload = byteArray.ToArray();
|
||||
}
|
||||
else if (payload is IEnumerable<int> intArray)
|
||||
{
|
||||
binaryPayload = intArray.Select(Convert.ToByte).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Payload type not supported.");
|
||||
}
|
||||
|
||||
applicationMessageBuilder = applicationMessageBuilder
|
||||
.WithPayload(binaryPayload);
|
||||
|
||||
var applicationMessage = applicationMessageBuilder.Build();
|
||||
|
||||
_mqttServer.PublishAsync(applicationMessage).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while publishing application message from server.");
|
||||
}
|
||||
}
|
||||
|
||||
private IMqttServerOptions CreateMqttServerOptions()
|
||||
{
|
||||
var options = new MqttServerOptionsBuilder()
|
||||
.WithMaxPendingMessagesPerClient(_settings.MaxPendingMessagesPerClient)
|
||||
.WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(_settings.CommunicationTimeout))
|
||||
.WithConnectionValidator(_mqttConnectionValidator)
|
||||
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
|
||||
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
|
||||
.WithStorage(_mqttServerStorage);
|
||||
|
||||
// Configure unencrypted connections
|
||||
if (_settings.TcpEndPoint.Enabled)
|
||||
{
|
||||
options.WithDefaultEndpoint();
|
||||
|
||||
if (_settings.TcpEndPoint.TryReadIPv4(out var address4))
|
||||
{
|
||||
options.WithDefaultEndpointBoundIPAddress(address4);
|
||||
}
|
||||
|
||||
if (_settings.TcpEndPoint.TryReadIPv6(out var address6))
|
||||
{
|
||||
options.WithDefaultEndpointBoundIPV6Address(address6);
|
||||
}
|
||||
|
||||
if (_settings.TcpEndPoint.Port > 0)
|
||||
{
|
||||
options.WithDefaultEndpointPort(_settings.TcpEndPoint.Port);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
options.WithoutDefaultEndpoint();
|
||||
}
|
||||
|
||||
// Configure encrypted connections
|
||||
if (_settings.EncryptedTcpEndPoint.Enabled)
|
||||
{
|
||||
options
|
||||
.WithEncryptedEndpoint()
|
||||
.WithEncryptionSslProtocol(SslProtocols.Tls12)
|
||||
.WithEncryptionCertificate(_settings.EncryptedTcpEndPoint.ReadCertificate());
|
||||
|
||||
if (_settings.EncryptedTcpEndPoint.TryReadIPv4(out var address4))
|
||||
{
|
||||
options.WithEncryptedEndpointBoundIPAddress(address4);
|
||||
}
|
||||
|
||||
if (_settings.EncryptedTcpEndPoint.TryReadIPv6(out var address6))
|
||||
{
|
||||
options.WithEncryptedEndpointBoundIPV6Address(address6);
|
||||
}
|
||||
|
||||
if (_settings.EncryptedTcpEndPoint.Port > 0)
|
||||
{
|
||||
options.WithEncryptedEndpointPort(_settings.EncryptedTcpEndPoint.Port);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
options.WithoutEncryptedEndpoint();
|
||||
}
|
||||
|
||||
if (_settings.ConnectionBacklog > 0)
|
||||
{
|
||||
options.WithConnectionBacklog(_settings.ConnectionBacklog);
|
||||
}
|
||||
|
||||
if (_settings.EnablePersistentSessions)
|
||||
{
|
||||
options.WithPersistentSessions();
|
||||
}
|
||||
|
||||
return options.Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttServerStorage : IMqttServerStorage
|
||||
{
|
||||
private readonly List<MqttApplicationMessage> _messages = new List<MqttApplicationMessage>();
|
||||
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly ILogger<MqttServerStorage> _logger;
|
||||
|
||||
private string _filename;
|
||||
private bool _messagesHaveChanged;
|
||||
|
||||
public MqttServerStorage(SettingsModel settings, ILogger<MqttServerStorage> logger)
|
||||
{
|
||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
if (_settings.RetainedApplicationMessages?.Persist != true)
|
||||
{
|
||||
_logger.LogInformation("Persisting of retained application messages is disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
_filename = ExpandFilename();
|
||||
|
||||
// The retained application messages are stored in a separate thread.
|
||||
// This is mandatory because writing them to a slow storage (like RaspberryPi SD card)
|
||||
// will slow down the whole message processing speed.
|
||||
Task.Run(SaveRetainedMessagesInternalAsync, CancellationToken.None);
|
||||
}
|
||||
|
||||
public Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages)
|
||||
{
|
||||
lock (_messages)
|
||||
{
|
||||
_messages.Clear();
|
||||
_messages.AddRange(messages);
|
||||
|
||||
_messagesHaveChanged = true;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SaveRetainedMessagesInternalAsync()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(_settings.RetainedApplicationMessages.WriteInterval)).ConfigureAwait(false);
|
||||
|
||||
List<MqttApplicationMessage> messages;
|
||||
lock (_messages)
|
||||
{
|
||||
if (!_messagesHaveChanged)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
messages = new List<MqttApplicationMessage>(_messages);
|
||||
_messagesHaveChanged = false;
|
||||
}
|
||||
|
||||
var json = JsonConvert.SerializeObject(messages);
|
||||
await File.WriteAllTextAsync(_filename, json, Encoding.UTF8).ConfigureAwait(false);
|
||||
|
||||
_logger.LogInformation($"{messages.Count} retained MQTT messages written.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while writing retained MQTT messages.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync()
|
||||
{
|
||||
if (_settings.RetainedApplicationMessages?.Persist != true)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!File.Exists(_filename))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = await File.ReadAllTextAsync(_filename).ConfigureAwait(false);
|
||||
var applicationMessages = JsonConvert.DeserializeObject<List<MqttApplicationMessage>>(json);
|
||||
|
||||
_logger.LogInformation($"{applicationMessages.Count} retained MQTT messages loaded.");
|
||||
|
||||
return applicationMessages;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogWarning(exception, "Error while loading persisted retained application messages.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string ExpandFilename()
|
||||
{
|
||||
var filename = _settings.RetainedApplicationMessages.Filename;
|
||||
|
||||
var uri = new Uri(filename, UriKind.RelativeOrAbsolute);
|
||||
if (!uri.IsAbsoluteUri)
|
||||
{
|
||||
filename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filename);
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MQTTnet.Server.Scripting;
|
||||
|
||||
namespace MQTTnet.Server.Mqtt
|
||||
{
|
||||
public class MqttSubscriptionInterceptor : IMqttServerSubscriptionInterceptor
|
||||
{
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MqttSubscriptionInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttSubscriptionInterceptor> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Task InterceptSubscriptionAsync(MqttSubscriptionInterceptorContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pythonContext = new PythonDictionary
|
||||
{
|
||||
{ "accept_subscription", context.AcceptSubscription },
|
||||
{ "close_connection", context.CloseConnection },
|
||||
|
||||
{ "client_id", context.ClientId },
|
||||
{ "topic", context.TopicFilter.Topic },
|
||||
{ "qos", (int)context.TopicFilter.QualityOfServiceLevel }
|
||||
};
|
||||
|
||||
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_subscription", pythonContext);
|
||||
|
||||
context.AcceptSubscription = (bool)pythonContext["accept_subscription"];
|
||||
context.CloseConnection = (bool)pythonContext["close_connection"];
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, "Error while intercepting subscription.");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
PrintLogo();
|
||||
|
||||
WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build().Run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintLogo()
|
||||
{
|
||||
Console.ResetColor();
|
||||
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 --
|
||||
Copyright (c) 2017-2019 The MQTTnet Team");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
Console.WriteLine(@"
|
||||
https://github.com/chkr1011/MQTTnet");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine($@"
|
||||
Version: {Assembly.GetExecutingAssembly().GetName().Version}
|
||||
License: MIT (read LICENSE file)
|
||||
Sponsoring: https://opencollective.com/mqttnet
|
||||
Support: https://github.com/chkr1011/MQTTnet/issues
|
||||
Docs: https://github.com/chkr1011/MQTTnet/wiki/MQTTnetServer
|
||||
");
|
||||
|
||||
Console.BackgroundColor = ConsoleColor.White;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(" ! THIS IS AN ALPHA VERSION! IT IS NOT RECOMMENDED TO USE IT FOR ANY DIFFERENT PURPOSE THAN TESTING OR EVALUATING!");
|
||||
Console.WriteLine();
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
[{"Topic":"a","Payload":"YWFzYXNhc2E=","QualityOfServiceLevel":0,"Retain":true,"UserProperties":[],"ContentType":null,"ResponseTopic":null,"PayloadFormatIndicator":null,"MessageExpiryInterval":null,"TopicAlias":null,"CorrelationData":null,"SubscriptionIdentifier":null},{"Topic":"fgdfgd","Payload":"YWFzYXNhc2E=","QualityOfServiceLevel":0,"Retain":true,"UserProperties":[],"ContentType":null,"ResponseTopic":null,"PayloadFormatIndicator":null,"MessageExpiryInterval":null,"TopicAlias":null,"CorrelationData":null,"SubscriptionIdentifier":null}]
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MQTTnet.Server.Scripting.DataSharing
|
||||
{
|
||||
public class DataSharingService
|
||||
{
|
||||
private readonly Dictionary<string, object> _storage = new Dictionary<string, object>();
|
||||
private readonly PythonScriptHostService _pythonScriptHostService;
|
||||
private readonly ILogger<DataSharingService> _logger;
|
||||
|
||||
public DataSharingService(PythonScriptHostService pythonScriptHostService, ILogger<DataSharingService> logger)
|
||||
{
|
||||
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
_pythonScriptHostService.RegisterProxyObject("write_shared_data", new Action<string, object>(Write));
|
||||
_pythonScriptHostService.RegisterProxyObject("read_shared_data", new Func<string, object, object>(Read));
|
||||
}
|
||||
|
||||
public void Write(string key, object value)
|
||||
{
|
||||
lock (_storage)
|
||||
{
|
||||
_storage[key] = value;
|
||||
_logger.LogInformation($"Shared data with key '{key}' updated.");
|
||||
}
|
||||
}
|
||||
|
||||
public object Read(string key, object defaultValue)
|
||||
{
|
||||
lock (_storage)
|
||||
{
|
||||
if (!_storage.TryGetValue(key, out var value))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using IronPython.Runtime;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public static class PythonConvert
|
||||
{
|
||||
public static object ToPython(object value)
|
||||
{
|
||||
if (value is PythonDictionary)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is string)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is int)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is float)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is bool)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is IDictionary dictionary)
|
||||
{
|
||||
var pythonDictionary = new PythonDictionary();
|
||||
foreach (DictionaryEntry dictionaryEntry in dictionary)
|
||||
{
|
||||
pythonDictionary.Add(dictionaryEntry.Key, ToPython(dictionaryEntry.Value));
|
||||
}
|
||||
|
||||
return pythonDictionary;
|
||||
}
|
||||
|
||||
if (value is IEnumerable enumerable)
|
||||
{
|
||||
var pythonList = new List();
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
pythonList.Add(ToPython(item));
|
||||
}
|
||||
|
||||
return pythonList;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static string Pythonfy(Enum value)
|
||||
{
|
||||
return Pythonfy(value.ToString());
|
||||
}
|
||||
|
||||
public static string Pythonfy(string value)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
foreach (var @char in value)
|
||||
{
|
||||
if (char.IsUpper(@char) && result.Length > 0)
|
||||
{
|
||||
result.Append('_');
|
||||
}
|
||||
|
||||
result.Append(char.ToLowerInvariant(@char));
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public static TEnum ParseEnum<TEnum>(string value) where TEnum : Enum
|
||||
{
|
||||
return (TEnum)Enum.Parse(typeof(TEnum), value.Replace("_", string.Empty), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public class PythonIOStream : Stream
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly Encoding _encoder = Encoding.UTF8;
|
||||
|
||||
public PythonIOStream(ILogger<PythonIOStream> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var text = _encoder.GetString(buffer, offset, count);
|
||||
if (text.Equals(Environment.NewLine))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogDebug(text);
|
||||
}
|
||||
|
||||
public override bool CanRead { get; } = false;
|
||||
public override bool CanSeek { get; } = false;
|
||||
public override bool CanWrite { get; } = true;
|
||||
public override long Length { get; } = 0L;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Scripting;
|
||||
using Microsoft.Scripting.Hosting;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public class PythonScriptHostService
|
||||
{
|
||||
private readonly IDictionary<string, object> _proxyObjects = new ExpandoObject();
|
||||
private readonly List<PythonScriptInstance> _scriptInstances = new List<PythonScriptInstance>();
|
||||
private readonly ILogger<PythonScriptHostService> _logger;
|
||||
private readonly ScriptEngine _scriptEngine;
|
||||
|
||||
public PythonScriptHostService(PythonIOStream pythonIOStream, ILogger<PythonScriptHostService> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_scriptEngine = IronPython.Hosting.Python.CreateEngine();
|
||||
_scriptEngine.Runtime.IO.SetOutput(pythonIOStream, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
AddSearchPaths(_scriptEngine);
|
||||
|
||||
var scriptsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Scripts");
|
||||
foreach (var filename in Directory.GetFiles(scriptsDirectory, "*.py", SearchOption.AllDirectories).OrderBy(file => file))
|
||||
{
|
||||
TryInitializeScript(filename);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterProxyObject(string name, object @object)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (@object == null) throw new ArgumentNullException(nameof(@object));
|
||||
|
||||
_proxyObjects.Add(name, @object);
|
||||
}
|
||||
|
||||
public void InvokeOptionalFunction(string name, object parameters)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
lock (_scriptInstances)
|
||||
{
|
||||
foreach (var pythonScriptInstance in _scriptInstances)
|
||||
{
|
||||
try
|
||||
{
|
||||
pythonScriptInstance.InvokeOptionalFunction(name, parameters);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, $"Error while invoking function '{name}' at script '{pythonScriptInstance.Name}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TryInitializeScript(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
var scriptName = new FileInfo(filename).Name;
|
||||
|
||||
_logger.LogTrace($"Initializing Python script '{scriptName}'...");
|
||||
var code = File.ReadAllText(filename);
|
||||
|
||||
var scriptInstance = CreateScriptInstance(scriptName, code);
|
||||
scriptInstance.InvokeOptionalFunction("initialize");
|
||||
|
||||
_scriptInstances.Add(scriptInstance);
|
||||
|
||||
_logger.LogInformation($"Initialized script '{scriptName}'.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.LogError(exception, $"Error while initializing script '{new FileInfo(filename).Name}'.");
|
||||
}
|
||||
}
|
||||
|
||||
private PythonScriptInstance CreateScriptInstance(string name, string code)
|
||||
{
|
||||
var scriptScope = _scriptEngine.CreateScope();
|
||||
|
||||
var source = scriptScope.Engine.CreateScriptSourceFromString(code, SourceCodeKind.File);
|
||||
var compiledCode = source.Compile();
|
||||
|
||||
scriptScope.SetVariable("mqtt_net_server", _proxyObjects);
|
||||
compiledCode.Execute(scriptScope);
|
||||
|
||||
return new PythonScriptInstance(name, scriptScope);
|
||||
}
|
||||
|
||||
private void AddSearchPaths(ScriptEngine scriptEngine)
|
||||
{
|
||||
var paths = new List<string>
|
||||
{
|
||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Lib"),
|
||||
"/usr/lib/python2.7",
|
||||
@"C:\Python27\Lib"
|
||||
};
|
||||
|
||||
AddSearchPaths(scriptEngine, paths);
|
||||
}
|
||||
|
||||
private void AddSearchPaths(ScriptEngine scriptEngine, IEnumerable<string> paths)
|
||||
{
|
||||
var searchPaths = scriptEngine.GetSearchPaths();
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
searchPaths.Add(path);
|
||||
_logger.LogInformation($"Added Python lib path: {path}");
|
||||
}
|
||||
}
|
||||
|
||||
scriptEngine.SetSearchPaths(searchPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using IronPython.Runtime;
|
||||
using Microsoft.Scripting.Hosting;
|
||||
|
||||
namespace MQTTnet.Server.Scripting
|
||||
{
|
||||
public class PythonScriptInstance
|
||||
{
|
||||
private readonly ScriptScope _scriptScope;
|
||||
|
||||
public PythonScriptInstance(string name, ScriptScope scriptScope)
|
||||
{
|
||||
_scriptScope = scriptScope;
|
||||
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public bool InvokeOptionalFunction(string name, params object[] parameters)
|
||||
{
|
||||
return InvokeOptionalFunction(name, parameters, out _);
|
||||
}
|
||||
|
||||
public bool InvokeOptionalFunction(string name, object[] parameters, out object result)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
lock (_scriptScope)
|
||||
{
|
||||
if (!_scriptScope.Engine.Operations.TryGetMember(_scriptScope, name, out var member))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(member is PythonFunction function))
|
||||
{
|
||||
throw new Exception($"Member '{name}' is no Python function.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result = _scriptScope.Engine.Operations.Invoke(function, parameters);
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var details = _scriptScope.Engine.GetService<ExceptionOperations>().FormatException(exception);
|
||||
var message = $"Error while invoking function '{name}'. " + Environment.NewLine + details;
|
||||
|
||||
throw new Exception(message, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import json
|
||||
|
||||
|
||||
def initialize():
|
||||
"""
|
||||
This function is invoked after the script file has been loaded.
|
||||
It will be executed only one time.
|
||||
"""
|
||||
|
||||
print("Hello World from Sample script.")
|
||||
|
||||
|
||||
def on_validate_client_connection(context):
|
||||
"""
|
||||
This function is invoked whenever a client wants to connect. It can be used to validate the connection.
|
||||
"""
|
||||
|
||||
print(context)
|
||||
|
||||
mqtt_net_server.write_shared_data(context["client_id"], {"custom_value_1": 1, "custom_value_2": True})
|
||||
|
||||
return
|
||||
|
||||
if context["client_id"] != "test_client":
|
||||
context["result"] = "connection_refused_not_authorized"
|
||||
return
|
||||
|
||||
if context["username"] != "bud spencer":
|
||||
context["result"] = "connection_refused_not_authorized"
|
||||
return
|
||||
|
||||
if context["password"] != "secret":
|
||||
context["result"] = "connection_refused_not_authorized"
|
||||
|
||||
print(context)
|
||||
|
||||
|
||||
def on_intercept_subscription(context):
|
||||
"""
|
||||
This function is invoked whenever a client wants to subscribe to a topic.
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' want's to subscribe to topic '{topic}'.".format(client_id=context["client_id"], topic=context["topic"]))
|
||||
|
||||
|
||||
def on_intercept_application_message(context):
|
||||
"""
|
||||
This function is invoked for every processed application message. It also allows modifying
|
||||
the message or cancel processing at all.
|
||||
"""
|
||||
|
||||
client_id = context["client_id"]
|
||||
|
||||
if client_id != None:
|
||||
shared_data = mqtt_net_server.read_shared_data(context["client_id"], {})
|
||||
print(shared_data)
|
||||
|
||||
if context["topic"] == "topic_with_response":
|
||||
|
||||
json_payload = {
|
||||
"hello": "world",
|
||||
"x": 1,
|
||||
"y": True,
|
||||
"z": None
|
||||
}
|
||||
|
||||
application_message = {
|
||||
"retain": False,
|
||||
"topic": "reply",
|
||||
"payload": json.dumps(json_payload)
|
||||
}
|
||||
|
||||
mqtt_net_server.publish(application_message)
|
||||
|
||||
print("Client '{client_id}' published topic '{topic}'.".format(client_id=context["client_id"], topic=context["topic"]))
|
||||
|
||||
|
||||
def on_client_connected(event_args):
|
||||
"""
|
||||
This function is called whenever a client has passed the validation is connected.
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' is now connected.".format(client_id=event_args["client_id"]))
|
||||
|
||||
|
||||
def on_client_disconnected(event_args):
|
||||
"""
|
||||
This function is called whenever a client has disconnected.
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' is now disconnected (type = {type}).".format(client_id=event_args["client_id"], type=event_args["type"]))
|
||||
|
||||
|
||||
def on_client_subscribed_topic(event_args):
|
||||
"""
|
||||
This function is called whenever a client has subscribed to a topic (when allowed).
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' has subscribed to '{topic}'.".format(client_id=event_args["client_id"], topic=event_args["topic"]))
|
||||
|
||||
|
||||
def on_client_unsubscribed_topic(event_args):
|
||||
"""
|
||||
This function is called whenever a client has unsubscribed from a topic.
|
||||
"""
|
||||
|
||||
print("Client '{client_id}' has unsubscribed from '{topic}'.".format(client_id=event_args["client_id"], topic=event_args["topic"]))
|
||||
@@ -0,0 +1,19 @@
|
||||
This directory contains scripts which are loaded by the server and can be used to perform the following tasks.
|
||||
|
||||
1. Validation of client connections via credentials, client IDs etc.
|
||||
2. Manipulation of every processed message.
|
||||
3. Validation of subscription attempts.
|
||||
4. Publishing of custom application messages.
|
||||
|
||||
All scripts are loaded and _MQTTnet Server_ will invoke functions according to predefined naming conventions.
|
||||
If a function is implemented in multiple scripts the context will be moved throug all instances. This allows overriding of results or passing data to other (following) scripts.
|
||||
|
||||
The Python starndard library ships with _MQTTnet Server_. But it is possible to add custom paths with Python libraries.
|
||||
|
||||
```
|
||||
import sys
|
||||
sys.path.append(PATH_TO_LIBRARY)
|
||||
```
|
||||
|
||||
* All scripts must have the file extension _.py_.
|
||||
* All scripts are sorted alphabetically (A to Z) before being loaded and parsed.
|
||||
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
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
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
public void Configure(
|
||||
IApplicationBuilder application,
|
||||
IHostingEnvironment environment,
|
||||
MqttServerService mqttServerService,
|
||||
PythonScriptHostService pythonScriptHostService,
|
||||
DataSharingService dataSharingService,
|
||||
SettingsModel settings)
|
||||
{
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
application.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
application.UseHsts();
|
||||
}
|
||||
|
||||
application.UseStaticFiles();
|
||||
|
||||
application.UseHttpsRedirection();
|
||||
application.UseMvc();
|
||||
|
||||
ConfigureWebSocketEndpoint(application, mqttServerService, settings);
|
||||
|
||||
dataSharingService.Configure();
|
||||
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)
|
||||
.AddJsonOptions(options =>
|
||||
{
|
||||
options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
|
||||
});
|
||||
|
||||
services.AddSingleton(ReadSettings());
|
||||
|
||||
services.AddSingleton<PythonIOStream>();
|
||||
services.AddSingleton<PythonScriptHostService>();
|
||||
services.AddSingleton<DataSharingService>();
|
||||
|
||||
services.AddSingleton<MqttNetLoggerWrapper>();
|
||||
services.AddSingleton<CustomMqttFactory>();
|
||||
services.AddSingleton<MqttServerService>();
|
||||
services.AddSingleton<MqttServerStorage>();
|
||||
|
||||
services.AddSingleton<MqttClientConnectedHandler>();
|
||||
services.AddSingleton<MqttClientDisconnectedHandler>();
|
||||
services.AddSingleton<MqttClientSubscribedTopicHandler>();
|
||||
services.AddSingleton<MqttClientUnsubscribedTopicHandler>();
|
||||
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()
|
||||
{
|
||||
var settings = new Configuration.SettingsModel();
|
||||
Configuration.Bind("MQTT", settings);
|
||||
return settings;
|
||||
}
|
||||
|
||||
private static void ConfigureWebSocketEndpoint(
|
||||
IApplicationBuilder application,
|
||||
MqttServerService mqttServerService,
|
||||
SettingsModel settings)
|
||||
{
|
||||
if (settings?.WebSocketEndPoint?.Enabled != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(settings.WebSocketEndPoint.Path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var webSocketOptions = new WebSocketOptions
|
||||
{
|
||||
KeepAliveInterval = TimeSpan.FromSeconds(settings.WebSocketEndPoint.KeepAliveInterval),
|
||||
ReceiveBufferSize = settings.WebSocketEndPoint.ReceiveBufferSize
|
||||
};
|
||||
|
||||
if (settings.WebSocketEndPoint.AllowedOrigins?.Any() == true)
|
||||
{
|
||||
webSocketOptions.AllowedOrigins.AddRange(settings.WebSocketEndPoint.AllowedOrigins);
|
||||
}
|
||||
|
||||
application.UseWebSockets(webSocketOptions);
|
||||
|
||||
application.Use(async (context, next) =>
|
||||
{
|
||||
if (context.Request.Path == settings.WebSocketEndPoint.Path)
|
||||
{
|
||||
if (context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false))
|
||||
{
|
||||
await mqttServerService.RunWebSocketConnectionAsync(webSocket, context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await next().ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"Kestrel": {
|
||||
"EndPoints": {
|
||||
"Http": {
|
||||
"Url": "http://localhost:80"
|
||||
},
|
||||
"Https": {
|
||||
"Url": "http://localhost:443"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MQTT": {
|
||||
/*
|
||||
Wildcard Addresses:
|
||||
* - All local IP addresses
|
||||
localhost - Localhost only
|
||||
disable - Skip address assignment
|
||||
*/
|
||||
"TcpEndPoint": {
|
||||
"Enabled": true,
|
||||
"IPv4": "*",
|
||||
"IPv6": "*",
|
||||
"Port": 1883
|
||||
},
|
||||
"EncryptedTcpEndPoint": {
|
||||
"Enabled": false,
|
||||
"IPv4": "*",
|
||||
"IPv6": "*",
|
||||
"Port": 8883,
|
||||
"CertificatePath": "/absolute/path/to/pfx"
|
||||
},
|
||||
"WebSocketEndPoint": {
|
||||
"Enabled": true,
|
||||
"Path": "/mqtt",
|
||||
"KeepAliveInterval": 120, // In seconds.
|
||||
"ReceiveBufferSize": 4096,
|
||||
"AllowedOrigins": [] // List of strings with URLs.
|
||||
},
|
||||
"CommunicationTimeout": 15, // In seconds.
|
||||
"ConnectionBacklog": 0, /* Set 0 to disable */
|
||||
"EnablePersistentSessions": false,
|
||||
"MaxPendingMessagesPerClient": 250,
|
||||
"RetainedApplicationMessages": {
|
||||
"Persist": true,
|
||||
"Filename": "RetainedApplicationMessages.json",
|
||||
"WriteInterval": 10 // In seconds.
|
||||
},
|
||||
"EnableDebugLogging": true
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"name": "mqtt.test",
|
||||
"private": true,
|
||||
"devDependencies": {},
|
||||
"dependencies": {
|
||||
"@types/node": "^8.0.46",
|
||||
"mqtt": "^2.15.1",
|
||||
"systemjs": "^0.20.19",
|
||||
"typescript": "^2.3.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
START "MQTTnet Server" dotnet .\MQTTnet.Server.dll
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
echo "Starting MQTTnet Server.."
|
||||
dotnet .\MQTTnet.Server.dll
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Serializer;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
@@ -10,17 +10,23 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
string Endpoint { get; }
|
||||
|
||||
IMqttPacketSerializer PacketSerializer { get; }
|
||||
bool IsSecureConnection { get; }
|
||||
|
||||
event EventHandler ReadingPacketStarted;
|
||||
MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
event EventHandler ReadingPacketCompleted;
|
||||
long BytesSent { get; }
|
||||
|
||||
long BytesReceived { get; }
|
||||
|
||||
Action ReadingPacketStartedCallback { get; set; }
|
||||
|
||||
Action ReadingPacketCompletedCallback { get; set; }
|
||||
|
||||
Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||
|
||||
Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||
|
||||
Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken);
|
||||
Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken);
|
||||
|
||||
Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public interface IMqttClientAdapterFactory
|
||||
{
|
||||
@@ -6,7 +6,7 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
public interface IMqttServerAdapter : IDisposable
|
||||
{
|
||||
event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
||||
Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
Task StartAsync(IMqttServerOptions options);
|
||||
Task StopAsync();
|
||||
|
||||
@@ -7,9 +7,9 @@ using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Serializer;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
@@ -22,41 +22,53 @@ namespace MQTTnet.Adapter
|
||||
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
private readonly IMqttChannel _channel;
|
||||
private readonly MqttPacketReader _packetReader;
|
||||
|
||||
private readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||
|
||||
private readonly byte[] _singleByteBuffer = new byte[1];
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
public MqttChannelAdapter(IMqttChannel channel, IMqttPacketSerializer serializer, IMqttNetChildLogger logger)
|
||||
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));
|
||||
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
|
||||
_packetReader = new MqttPacketReader(_channel);
|
||||
|
||||
_logger = logger.CreateChildLogger(nameof(MqttChannelAdapter));
|
||||
}
|
||||
|
||||
public string Endpoint => _channel.Endpoint;
|
||||
|
||||
public IMqttPacketSerializer PacketSerializer { get; }
|
||||
public bool IsSecureConnection => _channel.IsSecureConnection;
|
||||
|
||||
public event EventHandler ReadingPacketStarted;
|
||||
public event EventHandler ReadingPacketCompleted;
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
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();
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Verbose("Connecting [Timeout={0}]", timeout);
|
||||
|
||||
await Internal.TaskExtensions
|
||||
.TimeoutAfterAsync(ct => _channel.ConnectAsync(ct), timeout, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await _channel.ConnectAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(t => _channel.ConnectAsync(t), timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -75,11 +87,15 @@ namespace MQTTnet.Adapter
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Verbose("Disconnecting [Timeout={0}]", timeout);
|
||||
|
||||
await Internal.TaskExtensions
|
||||
.TimeoutAfterAsync(ct => _channel.DisconnectAsync(), timeout, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await _channel.DisconnectAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(
|
||||
t => _channel.DisconnectAsync(t), timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -92,16 +108,28 @@ namespace MQTTnet.Adapter
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var packetData = PacketSerializer.Serialize(packet);
|
||||
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
|
||||
PacketSerializer.FreeBuffer();
|
||||
var packetData = PacketFormatterAdapter.Encode(packet);
|
||||
|
||||
_logger.Verbose("TX >>> {0}", packet);
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(
|
||||
t => _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, t), timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Interlocked.Add(ref _bytesReceived, packetData.Count);
|
||||
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
|
||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -125,14 +153,13 @@ namespace MQTTnet.Adapter
|
||||
try
|
||||
{
|
||||
ReceivedMqttPacket receivedMqttPacket;
|
||||
|
||||
if (timeout > TimeSpan.Zero)
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
receivedMqttPacket = await Internal.TaskExtensions.TimeoutAfterAsync(ReceiveAsync, timeout, cancellationToken).ConfigureAwait(false);
|
||||
receivedMqttPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
receivedMqttPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
|
||||
receivedMqttPacket = await MqttTaskTimeout.WaitAsync(ReceiveAsync, timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
|
||||
@@ -140,16 +167,26 @@ namespace MQTTnet.Adapter
|
||||
return null;
|
||||
}
|
||||
|
||||
var packet = PacketSerializer.Deserialize(receivedMqttPacket);
|
||||
Interlocked.Add(ref _bytesSent, receivedMqttPacket.TotalLength);
|
||||
|
||||
if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket);
|
||||
}
|
||||
|
||||
var packet = PacketFormatterAdapter.Decode(receivedMqttPacket);
|
||||
if (packet == null)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Received malformed packet.");
|
||||
}
|
||||
|
||||
_logger.Verbose("RX <<< {0}", packet);
|
||||
_logger.Verbose("RX ({0} bytes) <<< {1}", receivedMqttPacket.TotalLength, packet);
|
||||
|
||||
return packet;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (IsWrappedException(exception))
|
||||
@@ -163,17 +200,34 @@ 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 fixedHeader = await MqttPacketReader.ReadFixedHeaderAsync(_channel, _fixedHeaderBuffer, _singleByteBuffer, cancellationToken).ConfigureAwait(false);
|
||||
var readFixedHeaderResult = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ReadingPacketStarted?.Invoke(this, EventArgs.Empty);
|
||||
if (readFixedHeaderResult.ConnectionClosed)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ReadingPacketStartedCallback?.Invoke();
|
||||
|
||||
var fixedHeader = readFixedHeaderResult.FixedHeader;
|
||||
if (fixedHeader.RemainingLength == 0)
|
||||
{
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, null);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, null, 2);
|
||||
}
|
||||
|
||||
var body = new byte[fixedHeader.RemainingLength];
|
||||
@@ -188,25 +242,27 @@ namespace MQTTnet.Adapter
|
||||
chunkSize = bytesLeft;
|
||||
}
|
||||
|
||||
#if WINDOWS_UWP
|
||||
var readBytes = await _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
// async/await is not used to avoid the overhead of context switches. We assume that the reamining 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
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ExceptionHelper.ThrowIfGracefulSocketClose(readBytes);
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (readBytes == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
bodyOffset += readBytes;
|
||||
} while (bodyOffset < body.Length);
|
||||
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(body, 0, body.Length));
|
||||
var bodyReader = new MqttPacketBodyReader(body, 0, body.Length);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReadingPacketCompleted?.Invoke(this, EventArgs.Empty);
|
||||
ReadingPacketCompletedCallback?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,17 +283,22 @@ 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)
|
||||
{
|
||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
|
||||
exception = innerException;
|
||||
}
|
||||
|
||||
if (exception is SocketException socketException)
|
||||
{
|
||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted ||
|
||||
socketException.SocketErrorCode == SocketError.OperationAborted)
|
||||
{
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public class MqttConnectingFailedException : MqttCommunicationException
|
||||
{
|
||||
public MqttConnectingFailedException(MqttConnectReturnCode returnCode)
|
||||
: base($"Connecting with MQTT server failed ({returnCode}).")
|
||||
public MqttConnectingFailedException(MqttClientConnectResultCode resultCode)
|
||||
: base($"Connecting with MQTT server failed ({resultCode.ToString()}).")
|
||||
{
|
||||
ReturnCode = returnCode;
|
||||
ResultCode = resultCode;
|
||||
}
|
||||
|
||||
public MqttConnectReturnCode ReturnCode { get; }
|
||||
public MqttClientConnectResultCode ResultCode { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,20 @@
|
||||
using MQTTnet.Serializer;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public class ReceivedMqttPacket
|
||||
{
|
||||
public ReceivedMqttPacket(byte fixedHeader, MqttPacketBodyReader body)
|
||||
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,80 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
public static class ApplicationMessagePublisherExtensions
|
||||
{
|
||||
public static async Task PublishAsync(this IApplicationMessagePublisher publisher, IEnumerable<MqttApplicationMessage> applicationMessages)
|
||||
{
|
||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||
|
||||
foreach (var applicationMessage in applicationMessages)
|
||||
{
|
||||
await publisher.PublishAsync(applicationMessage).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task PublishAsync(this IApplicationMessagePublisher publisher, params MqttApplicationMessage[] applicationMessages)
|
||||
{
|
||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||
|
||||
foreach (var applicationMessage in applicationMessages)
|
||||
{
|
||||
await publisher.PublishAsync(applicationMessage).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic)
|
||||
{
|
||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return publisher.PublishAsync(builder => builder
|
||||
.WithTopic(topic));
|
||||
}
|
||||
|
||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload)
|
||||
{
|
||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return publisher.PublishAsync(builder => builder
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload));
|
||||
}
|
||||
|
||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return publisher.PublishAsync(builder => builder
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel));
|
||||
}
|
||||
|
||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
||||
{
|
||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return publisher.PublishAsync(builder => builder
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
||||
.WithRetainFlag(retain));
|
||||
}
|
||||
|
||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
|
||||
{
|
||||
var message = builder(new MqttApplicationMessageBuilder()).Build();
|
||||
return publisher.PublishAsync(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,10 @@ namespace MQTTnet.Channel
|
||||
public interface IMqttChannel : IDisposable
|
||||
{
|
||||
string Endpoint { get; }
|
||||
bool IsSecureConnection { get; }
|
||||
|
||||
Task ConnectAsync(CancellationToken cancellationToken);
|
||||
Task DisconnectAsync();
|
||||
Task DisconnectAsync(CancellationToken cancellationToken);
|
||||
|
||||
Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
|
||||
Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public interface IMqttClientConnectedHandler
|
||||
{
|
||||
Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public class MqttClientAuthenticateResult
|
||||
{
|
||||
public bool IsSessionPresent { get; set; }
|
||||
|
||||
public MqttClientConnectResultCode ResultCode { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public enum MqttClientConnectResultCode
|
||||
{
|
||||
Success = 0,
|
||||
UnspecifiedError = 128,
|
||||
MalformedPacket = 129,
|
||||
ProtocolError = 130,
|
||||
ImplementationSpecificError = 131,
|
||||
UnsupportedProtocolVersion = 132,
|
||||
ClientIdentifierNotValid = 133,
|
||||
BadUserNameOrPassword = 134,
|
||||
NotAuthorized = 135,
|
||||
ServerUnavailable = 136,
|
||||
ServerBusy = 137,
|
||||
Banned = 138,
|
||||
BadAuthenticationMethod = 140,
|
||||
TopicNameInvalid = 144,
|
||||
PacketTooLarge = 149,
|
||||
QuotaExceeded = 151,
|
||||
PayloadFormatInvalid = 153,
|
||||
RetainNotSupported = 154,
|
||||
QoSNotSupported = 155,
|
||||
UseAnotherServer = 156,
|
||||
ServerMoved = 157,
|
||||
ConnectionRateExceeded = 159
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public class MqttClientConnectedEventArgs : EventArgs
|
||||
{
|
||||
public MqttClientConnectedEventArgs(MqttClientAuthenticateResult authenticateResult)
|
||||
{
|
||||
AuthenticateResult = authenticateResult ?? throw new ArgumentNullException(nameof(authenticateResult));
|
||||
}
|
||||
|
||||
public MqttClientAuthenticateResult AuthenticateResult { get; }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user