Compare commits

...
136 Commits
Author SHA1 Message Date
Christian d5691346a9 Update MQTTnet.nuspec 2022-01-27 20:22:51 +01:00
HansM 0730f21d29 Merge pull request #1338 from alvin1221/public_broker_test
Thank you, this is indeed a bug.
2022-01-12 17:57:33 +01:00
alvin1221 6dfa7f0769 Fix incorrect TLS port of broker.emqx.io 2022-01-12 11:29:15 +08:00
Christian 27592f1643 Update MQTTnet.nuspec 2022-01-08 15:36:35 +01:00
Christian c6f4b59438 Increase delay for Keep Alive checks to decrease CPU load. 2022-01-08 15:35:47 +01:00
Christian b362b2afc2 Make logger parameters generic. 2022-01-08 14:35:25 +01:00
Christian 7a72a8d562 Update MQTTnet.nuspec 2022-01-08 14:07:57 +01:00
Günther Foidl bd40e10a66 Elide repeated Action-delegate allocation by caching it (#1324)
* Elide repeated Action-delegate allocation by caching it

Here C# compiler has too little knowledge about the usage of the delegate, we know better so cache it on our own to avoid the repeated allocation.

* Use conditional compilation
2022-01-08 14:00:39 +01:00
Christian 4b44a2b2b1 Remove appveyor. 2022-01-07 18:01:03 +01:00
Christian 215f24a95b Create CODE-OF-CONDUCT.md 2022-01-07 17:52:16 +01:00
Christian 72f9acdc20 Update CI 2022-01-06 20:55:20 +01:00
Christian 1e5390679f Update ci.yml 2022-01-06 20:41:18 +01:00
Christian 5cafce4345 Update ci.yml 2022-01-06 20:40:06 +01:00
Christian dfaf3dc31f Update ci.yml 2022-01-06 20:37:24 +01:00
Christian 3a9397c793 Update CI. 2022-01-06 20:36:12 +01:00
Christian 1cf4a7d6be Update ci.yml 2022-01-06 20:26:11 +01:00
Christian 180588c3a4 Update ci.yml 2022-01-06 20:18:04 +01:00
Christian bc4c86bebd Update ci.yml 2022-01-06 20:13:11 +01:00
Christian ea88a8c0de Update CI. 2022-01-06 20:10:42 +01:00
Günther Foidl d98129481a Fixed latent bug in MqttApplicationMessageBuilder.WithPayload stream overload (#1322) 2022-01-04 19:25:26 +01:00
Christian 5e5818d7df Merge branch 'master' of https://github.com/chkr1011/MQTTnet 2021-11-28 14:45:27 +01:00
Christian 72c7a8959c Update MQTTnet.nuspec 2021-11-28 12:57:22 +01:00
patagona 39e637795e improve exception message when handling connection errors (#1302) 2021-11-28 12:56:32 +01:00
logicaloud 731c89d2e9 Fix MQTT5 'IsPersisted' session flag handling (#1300) 2021-11-28 12:51:29 +01:00
Michi 25ddfc83a8 MqttRpcClient now adds ResponseTopic to MqttApplicationMessage when using MQTT v5 (#1295)
* MqttRpcClient now adds ResponseTopic to MqttApplicationMessage when using MQTT v5

* No need for conditional protocol check (ResponseTopic property won't be added by PacketFormatter in <MQTT v5)

* Cleanup: Remove using
2021-11-28 12:41:56 +01:00
Christian 2d94abdde3 Update MQTTnet.nuspec
Update docs.
2021-11-13 10:09:59 +01:00
logicaloud f0ea13c07f Fix MQTT 3.1.1 session persistence when CleanSession=1 (#1294) 2021-11-13 10:05:54 +01:00
Volker Milbrandt 47cd5aa3cd test ResultCode for invalid credentials (no userName but password) (#1270)
fix order of parameters in exceptions AreEqual(expected, actual)
2021-11-13 10:02:55 +01:00
HansM 258df6bb14 Removed IDisposable from MqttClientConnection class. (#1288) 2021-11-11 20:10:35 +01:00
SeppPenner 85c3c0b352 MQTTnet.noUWP.sln: Removed no longer present MQTTnet.Server project. 2021-11-08 08:50:59 +01:00
Christian 8020e66f41 Fix UWP build errors. 2021-11-01 12:28:39 +01:00
Christian bd004ec327 Update docs. 2021-10-30 10:21:50 +02:00
logicaloud 1e68c2e815 Fixes #1276 timestamp check in server keep-alive monitor (#1277)
* Fix timestamp check in server keep-alive monitor

* Retain the original client statistics property name
- add comments to clarify meaning
- add separate public "received" timestamp property

* fix comment
2021-10-30 10:12:57 +02:00
Christian d7e1c89d62 Update docs. 2021-10-27 22:20:55 +02:00
patagona b779d04636 let the OS choose the supported TLS ptotocols when connecting to a MQTT broker (#1271) 2021-10-23 10:31:44 +02:00
logicaloud 623d8cafaa Fix #1263 - Set IsSessionPresent in CONNACK if client session exists (#1268)
* Set IsSessionPresent in CONNACK if client session exists

* Set IsSessionPresent in CONNACK if client session exists;
- restore white space differences to make comparison easier
2021-10-23 10:28:08 +02:00
Christian 0920c86d37 Refactor logger. (#1247) 2021-09-26 11:05:00 +02:00
Christian c756a4092f Feature/mqttv5 subscriptions server (#1246)
* Add support for RetainHandling.

* Add support for No Local.

* Add support for _SubscriptionIdentifier_.

* Add more Unit Tests.

* Update docs.
2021-09-23 20:30:43 +02:00
Christian feb53e78c6 Update docs. 2021-09-19 13:02:15 +02:00
Ablfzl Edgelolliandablfzl dd0aba0640 Process all queued message before server stops (#1234)
* process all queued message befor server stops

* fix an issue

Co-authored-by: ablfzl <ablfzl21@outlook.com>
2021-09-19 13:01:47 +02:00
Christian 68f1043ef5 Refactor sub/unsub management in managed client. 2021-09-19 12:59:32 +02:00
scottbrogden-iheartmedia d01d859227 Added option for how many subscriptions to send in a single call to t… (#1209)
* Added option for how many subscriptions to send in a single call to the server.  aws iotcore limits this to 8

* Split subscription messages up to 8 at a time when reconnecting
2021-09-19 12:41:33 +02:00
Marc Sallin fcab0d5cf0 Add Silverback to references in readme (#1238) 2021-09-19 12:35:19 +02:00
HansM b66a18f8dd Merge pull request #1241 from soyoo/master
Update README.md
2021-09-15 19:33:13 +02:00
zhengshuyuan 8e1e93c00f Update README.md
fix typo
2021-09-15 21:10:49 +08:00
Christian cd8940f9b7 Improve internal server connection management. (#1232) 2021-09-08 18:01:56 +02:00
Jacky Jiang 9d9077f17c Update MqttApplicationMessageBuilder.cs (#1230)
update function WithMessageExpiryInterval description
2021-09-08 15:45:18 +02:00
Christian fa6859d68a Merge branch 'master' of https://github.com/chkr1011/MQTTnet 2021-09-05 13:32:50 +02:00
Christian 4f14373117 Add Unit Tests. 2021-09-05 13:32:46 +02:00
Christian 90ec39f2a4 Refactor code. 2021-09-05 13:31:36 +02:00
Christian 2aa4d1b77a Add low level client to test environment. 2021-09-05 13:31:23 +02:00
Christian aa9f8d8ef3 Update release notes. 2021-09-05 13:30:39 +02:00
Nikita Senkov 50381637af Updated _ReconnectAsync_ extension methods to retrieve authentication result (#1202) 2021-09-05 11:49:52 +02:00
Harry Kakoulidis 8905b612c9 Updated tests with public servers. (#1223)
* Updated tests with public servers.

* Separated tests with TLS12 and TLS13.
2021-09-05 11:45:30 +02:00
HansM 3cdf6d1261 [MQTTnet, MQTTnet.Extensions.ManagedClient] Fixed bug that allowed invalid subscriptions (Thanks to @marcelwinh). (#1226) 2021-09-05 11:42:42 +02:00
Christian d1c883ebae Move MQTTnet.Server to dedicated repo. 2021-06-29 20:18:20 +02:00
Christian ed5e6ee897 Update project files. 2021-06-28 20:39:10 +02:00
Christian 2e019f372c Update docs. 2021-06-27 17:02:04 +02:00
Christian cc9bc2d3a0 Merge remote-tracking branch 'origin/master' 2021-06-27 16:53:54 +02:00
Christian 306bce5a58 Refactor code. 2021-06-27 16:53:44 +02:00
Ramon Smits 6fe7ce7a0c FIxed MqttNetLogLevel.Verbose incorrectly mapped to LogLevel.Debug (#1170)
Based on https://github.com/chkr1011/MQTTnet/issues/1138#issuecomment-846649937
2021-05-30 22:01:38 +02:00
Christian 8f1d4e3c22 Remove client disconnected handler from wrong implementation location. 2021-04-11 20:34:35 +02:00
Christian 77338527b4 Refactor code. 2021-04-05 12:50:42 +02:00
Christian 962622a9f7 Merge pull request #1134 from yyjdelete/issue996
Disallow to call MqttClient.ConnectAsync while Disconnect is pending.
2021-04-05 11:19:20 +02:00
SilverFox 899ed7b1da Rename MqttClient.ConnectState to MqttClientConnectionStatus 2021-04-03 13:19:50 +08:00
Christian 10f7d809f2 Merge pull request #1132 from qzwzqty/master
Passes the Endpoint of the client before it disconnected
2021-04-02 16:18:15 +02:00
Christian 324fb7e48c Merge pull request #1136 from yyjdelete/framework
Fix UWP support broken in #1135
2021-04-02 16:17:13 +02:00
SilverFox d913d4402e Convert MqttClient._connectState to enum 2021-04-02 21:01:49 +08:00
SilverFox df262b5588 Fix UWP support broken in #1135 2021-04-02 19:58:03 +08:00
Christian 27daf37ba4 Update MQTTnet.nuspec 2021-04-02 12:49:31 +02:00
Christian e5e10eee6d Merge pull request #1135 from yyjdelete/framework
Add more checks for NET5_0
2021-04-02 12:48:43 +02:00
SilverFox e174546b5a Add more checks for NET5_0 2021-04-02 18:03:44 +08:00
SilverFox f99daee4d1 Fix Session_Tests.Manage_Session_MaxParallel 2021-04-02 17:54:41 +08:00
SilverFox 9b10eadb24 Fix broken tests 2021-04-02 17:54:41 +08:00
SilverFox c39da42ef2 Disallow to call MqttClient.ConnectAsync while Disconnect is pending.
Fix #996
Fix #1010
2021-04-01 22:00:01 +08:00
Wang Chunlong 85b339e6f0 rename 2021-03-31 11:56:58 +08:00
Christian fbb5cd45a0 Refactor deferred ACK of publish packets. 2021-03-30 21:06:44 +02:00
Christian d430ca0250 Merge pull request #1075 from tkurucsai/feature/pending-appmsg-acknowledge
Feature/pending appmsg acknowledge
2021-03-30 14:34:43 +02:00
Wang Chunlong cf8ea872d3 Disconnected Endpoint 2021-03-29 16:00:37 +08:00
Christian d4440f3be1 Merge remote-tracking branch 'origin/master' 2021-03-21 20:42:25 +01:00
Christian b97d01b78b Fix wrong data validation (#342). 2021-03-21 20:42:09 +01:00
Christian 375ee5c4ac Merge pull request #1121 from asymetrixs/master
Added workflow to  build debian/ubuntu packages
2021-03-21 20:29:02 +01:00
tamas.kurucsai 90b37a1e2d reimplemented delayed acknowledgement using a property to switch off automatic acknowledgement and a new method Acknowledge() in the event handler 2021-03-20 10:36:44 +01:00
Damian 0978f6cc75 Merge pull request #7 from asymetrixs/deb-packaging
Deb packaging
2021-03-12 18:04:25 +01:00
Damian Wolgast e7c51e8dbc Extraction of Version from project file. 2021-03-12 18:03:32 +01:00
Damian Wolgast aab4448980 Debugging 2021-03-12 18:01:31 +01:00
Damian Wolgast 52139c6a99 Debugging 2021-03-12 17:56:34 +01:00
Damian Wolgast f3d121120b Debugging 2021-03-12 17:51:18 +01:00
Damian Wolgast 413c68de44 Debugging 2021-03-12 17:47:53 +01:00
Damian Wolgast f0919487f2 Debugging 2021-03-12 17:44:26 +01:00
Damian ffa7c51899 Merge pull request #6 from asymetrixs/deb-packaging
Deb packaging
2021-03-12 17:38:07 +01:00
Damian Wolgast 493cad087f Fixed syntax 2021-03-12 17:36:41 +01:00
Damian Wolgast 4d94e1a318 Debugging 2021-03-12 17:32:03 +01:00
Damian 28ff08ceb9 Merge pull request #5 from asymetrixs/deb-packaging
Read version from MQTTnet.Server project file
2021-03-12 17:28:03 +01:00
Damian Wolgast 0f3d461c37 Read version from MQTTnet.Server project file 2021-03-12 17:27:18 +01:00
Damian 4285169108 Merge pull request #4 from asymetrixs/deb-packaging
Added dependency to libssl
2021-03-12 15:44:58 +01:00
Damian Wolgast 1b1f2db1e6 Added dependency to libssl 2021-03-12 15:44:22 +01:00
Damian d7248c0f64 Merge pull request #3 from asymetrixs/deb-packaging
Added check for availability of setcap
2021-03-12 15:20:08 +01:00
Damian Wolgast d2e1b12186 Added check for availability of setcap 2021-03-12 15:18:13 +01:00
Damian dafcfb47d2 Merge pull request #2 from asymetrixs/deb-packaging
Docker support
2021-03-12 14:41:38 +01:00
Damian Wolgast 897f0f36d1 Fixed spelling 2021-03-12 14:40:21 +01:00
Damian Wolgast 9cb12ac2ce Docker, check if systemctl exists before using it 2021-03-12 14:39:51 +01:00
Damian 312fb2bb3c Merge pull request #1 from asymetrixs/deb-packaging
Fixed user creation to not copy skel-files
2021-03-12 14:19:24 +01:00
Damian Wolgast 3ee785c781 Fixed user creation to not copy skel-files 2021-03-12 14:17:38 +01:00
Damian Wolgast 0ea4ae7501 Fix, forgot to rename file. 2021-03-12 12:40:02 +01:00
Damian Wolgast 7e764c7f0f Added revision (assuming 1) and architecture 2021-03-12 12:36:14 +01:00
Damian Wolgast 5c2879029b Added command to stop service before uninstalling 2021-03-12 12:26:04 +01:00
Damian Wolgast 6cbb8d42e3 Added prerm to revoke service permissions 2021-03-12 12:15:49 +01:00
Damian Wolgast d8d50f6dcb Added workflow to build debian/ubuntu packages 2021-03-11 18:19:25 +01:00
Christian a01dc9fef8 Add new overload for server options builder. 2021-03-05 13:01:14 +01:00
tamas.kurucsai 3427d82efc Merge branch 'master' of https://github.com/chkr1011/MQTTnet into feature/pending-appmsg-acknowledge
# Conflicts:
#	Source/MQTTnet/Client/MqttClient.cs
2021-03-05 12:10:01 +01:00
Christian 5915285522 Fix TLS parameter options builder (#1104). 2021-03-02 10:00:34 +01:00
Christian 12f346115f Improve RPC client handling. 2021-02-26 10:55:26 +01:00
Christian 05827341d4 Update codeql-analysis.yml 2021-02-23 12:05:24 +01:00
Christian 1e88d6a05b Merge remote-tracking branch 'origin/master' 2021-02-23 11:46:38 +01:00
Christian 8261ba9dc1 Update docs. 2021-02-23 11:46:16 +01:00
Christian b766c1bebb Refactor keep alive message handling in order to fix issues with sudden disconnects. 2021-02-23 11:45:22 +01:00
Christian 6c188899fb Create codeql-analysis.yml 2021-02-21 11:33:51 +01:00
tamas.kurucsai 23c5270c3f Merge branch 'master' of https://github.com/chkr1011/MQTTnet into feature/pending-appmsg-acknowledge
# Conflicts:
#	Source/MQTTnet/Client/MqttClient.cs
#	Source/MQTTnet/MqttApplicationMessageReceivedEventArgs.cs
2021-02-20 12:55:28 +01:00
Christian 2d4ea8ee9b Improve nuget packages. 2021-02-19 21:52:42 +01:00
Christian 15d3761843 Merge pull request #1097 from yyjdelete/nuspec
Update nuspecs
2021-02-19 21:50:49 +01:00
Christian ed520455d0 Use the data converter only when creating packets. 2021-02-19 21:45:23 +01:00
Christian cfc4ebe6a3 Expose Dup flag in application messages (#1096). 2021-02-19 21:39:23 +01:00
Christian 120b7fb7d1 Fix RPC issue #1093 by calling both message handlers. 2021-02-18 18:56:15 +01:00
SilverFox 8b164a4846 Update nuspecs
Manually sync with the file generated by `msbuild /t:pack` or `dotnet pack`, and remove some unnessary dependencies
2021-02-18 11:26:54 +08:00
Christian fda4272611 Update docs. 2021-02-11 12:31:13 +01:00
Christian 88cc79d52f Fix issues in nuspecs. 2021-02-11 12:25:58 +01:00
Christian 8ba960343c Fix exception handling issues (#1082). 2021-02-08 13:29:15 +01:00
Christian 0976fa682a Merge pull request #1083 from milbrandt/bugfix/noUsername
Test for Regression: No exception on ConnectAsync(null, password) [MQTT-3.1.2-22]
2021-02-06 13:38:04 +01:00
Christian 18cc454842 Fix build script for .net5.0. 2021-02-06 13:36:23 +01:00
Volker Milbrandt e342a51d50 Merge branch 'master' 2021-02-04 21:23:02 +01:00
Volker Milbrandt 57652c76cf tests with MqttClient.ConnectAsync with credentials
for all four combinations of null/non-null for username and password
2021-02-04 21:09:36 +01:00
tamas.kurucsai fbf7d9bb5c corrected thread safety of ping timers after adding delayed acknowledgement 2021-01-29 07:03:56 +01:00
tamas.kurucsai 822f582e41 added test for delayed message acknowledgement 2021-01-29 07:03:19 +01:00
tamas.kurucsai fcbe2e32f6 corrected name of response sender method 2021-01-18 17:58:49 +01:00
tamas.kurucsai f955b4a3e6 implemented delayed message acknowledgement 2021-01-18 10:47:21 +01:00
264 changed files with 5957 additions and 6627 deletions
+34
View File
@@ -0,0 +1,34 @@
name: CI
on: [push]
jobs:
build:
runs-on: windows-latest
strategy:
matrix:
dotnet-version: ['6.0.x']
steps:
- uses: actions/checkout@v2
- name: Setup .NET Core SDK ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v1.7.2
with:
dotnet-version: ${{ matrix.dotnet-version }}
- name: Build
shell: pwsh
run: |
Set-Location -Path "Build"
.\build.ps1 1.2.3 4.5.6-rc1
# - name: Install dependencies
# run: dotnet restore MQTTnet.sln
# - name: Build
# run: dotnet build --configuration Release --no-restore MQTTnet.sln
# - name: Test MQTTnet
# run: dotnet test --no-restore --verbosity normal Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj
# - name: Test AspNetCore
# run: dotnet test --no-restore --verbosity normal Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj
+2
View File
@@ -294,3 +294,5 @@ __pycache__/
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
Build/NuGet/
Build/nuget.exe
+2 -2
View File
@@ -12,7 +12,7 @@
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright>
<copyright>Copyright Christian Kratky 2016-2021</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 Blazor</tags>
<dependencies>
<group targetFramework="netstandard2.0">
@@ -38,7 +38,7 @@
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\net5.0\MQTTnet.AspNetCore.*" target="lib\net5.0\" />
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\" />
@@ -12,7 +12,7 @@
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright>
<copyright>Copyright Christian Kratky 2016-2021</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 Blazor</tags>
<dependencies>
<dependency id="MQTTnet" version="$nugetVersion" />
@@ -27,7 +27,7 @@
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net5.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\" />
@@ -35,6 +35,9 @@
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.0\" />
<!-- .NET Standard 2.1 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.1\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.1\" />
<!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\uap10.0\MQTTnet.Extensions.ManagedClient.*" target="lib\uap10.0\" />
@@ -42,7 +45,7 @@
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\" />
<!-- .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\" />
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net461\MQTTnet.Extensions.ManagedClient.*" target="lib\net461\" />
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files>
+5 -2
View File
@@ -12,7 +12,7 @@
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright>
<copyright>Copyright Christian Kratky 2016-2021</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 Blazor</tags>
<dependencies>
<dependency id="MQTTnet" version="$nugetVersion" />
@@ -27,7 +27,7 @@
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net5.0\MQTTnet.Extensions.Rpc.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\" />
@@ -35,6 +35,9 @@
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\" />
<!-- .NET Standard 2.1 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.1\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.1\" />
<!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\" />
+7 -18
View File
@@ -12,25 +12,11 @@
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<description>This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright>
<copyright>Copyright Christian Kratky 2016-2021</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 Blazor</tags>
<dependencies>
<group targetFramework="netstandard1.3">
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="WebSocket4Net" version="0.15.2" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="WebSocket4Net" version="0.15.2" />
</group>
<group targetFramework="netstandard2.1">
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="WebSocket4Net" version="0.15.2" />
</group>
<group targetFramework="uap10.0">
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="WebSocket4Net" version="0.15.2" />
</group>
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="WebSocket4Net" version="0.15.2" />
</dependencies>
</metadata>
@@ -42,7 +28,7 @@
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net5.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard1.3\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard1.3\" />
@@ -50,6 +36,9 @@
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.0\" />
<!-- .NET Standard 2.1 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.1\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.1\" />
<!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\uap10.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\uap10.0\" />
+12 -38
View File
@@ -12,54 +12,28 @@
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</description>
<releaseNotes>
* [Core] Added .NET 5.0 TFMs (thanks to @StefanOssendorf, @JanEggers).
* [Core] Added support for TLS 1.3 (requires .NET Core 3.1+) (thanks to @Dvergatal).
* [Core] Added support for _MaximumQoS_ transmit when using MQTTv5 (thanks to @nayato).
* [Core] Aligned the format of some log messages.
* [Client] Fixed an issue with connection state detection (thanks to @tobiasfrick).
* [Client] Fixed an issue when receiving partial QoS 2 packets after a reconnect.
* [Client] Added new API for raw packet inspection.
* [ManagedClient] Removed _AutoReconnect_ since this does no longer work (BREAKING CHANGE!).
* [RpcClient] Moved the RPC topic validation to the topic generation strategy.
* [RcpClient] Added interface for _MqttRpcClient_.
* [Server] Reduced async tasks count by moving dedicated keep alive tasks per connection to shared one.
* [Server] Session takeover and keep alive timeout are now properly set in DISCONNECT packet.
* [Server] Fixed bug in PubRel packet generation (MQTTv5 only).
* [Server] Improved message processing performance (+ ~5%).
* [Server] Fix wrong usage of client session items for undelivered messages.
* [Server] Allow to respond with a reason code in PUBACK/PUBREC (thanks to @muneebmajeed).
* [Server] Fixed topic filter comparer on edge cases, e.g. "foo/" matching "foo/#" (thanks to @dagophil).
* [Core] Added code documentation.
* [Client] Increased delay for keep alive checks do decrease CPU load.
* [Core] Decreased object allocations (#1324, thanks to @gfoidl).
* [Core] Decreased object allocations when logging is not active (thanks to @gfoidl, @Tymoniden).
* [Client] Fixed issue in _MqttApplicationMessageBuilder.WithPayload_ (#1322, thanks to @gfoidl).
Git commit: $gitCommit
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright>
<copyright>Copyright Christian Kratky 2016-2021</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 Blazor</tags>
<dependencies>
<group targetFramework="net452" />
<group targetFramework="net461" />
<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>
<group targetFramework="netstandard2.1">
<dependency id="NETStandard.Library" version="2.0.0" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="netcoreapp3.1">
<dependency id="NETStandard.Library" version="2.0.0" />
<dependency id="NETStandard.Library" version="1.6.1" />
<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" />
<group targetFramework="netstandard2.1" />
<group targetFramework="netcoreapp3.1" />
<group targetFramework="net5.0" />
<group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" />
</group>
+9 -49
View File
@@ -5,6 +5,7 @@ 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
$vstest = &$vswhere -products * -latest -find **\TestWindow\**\vstest.console.exe
Write-Host
Write-Host "Assembly version = $assemblyVersion"
@@ -20,8 +21,8 @@ Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.
&$msbuild ..\Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /verbosity:m
&$msbuild ..\Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /verbosity:m
vstest.console.exe ..\Tests\MQTTnet.Core.Tests\bin\Release\netcoreapp3.1\MQTTnet.Tests.dll
vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.Tests.dll
&$vstest ..\Tests\MQTTnet.Core.Tests\bin\Release\netcoreapp3.1\MQTTnet.Tests.dll
&$vstest ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.Tests.dll
# 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"
@@ -31,10 +32,12 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.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"
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.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"
@@ -43,6 +46,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$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="netstandard2.1" /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"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.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"
@@ -51,6 +55,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$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="netstandard2.1" /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"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the WebSocket4Net extension
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.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"
@@ -59,6 +64,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.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.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net5.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Create NuGet packages.
@@ -90,50 +96,4 @@ Move-Item MQTTnet.Extensions.Rpc.nuspec.old -Destination MQTTnet.Extensions.Rpc.
Move-Item MQTTnet.Extensions.ManagedClient.nuspec.old -Destination MQTTnet.Extensions.ManagedClient.nuspec -Force
Move-Item MQTTnet.Extensions.WebSocket4Net.nuspec.old -Destination MQTTnet.Extensions.WebSocket4Net.nuspec -Force
Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
####################################################################
# Build MQTTnet.Server Portable
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\publish"
$destination = (Convert-Path .) + "\..\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)
####################################################################
# Build MQTTnet.Server Linux-x64
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-x64 --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-x64\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-x64-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
####################################################################
# Build MQTTnet.Server Linux-ARM
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-arm --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-ARM\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-ARM-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
####################################################################
# Build MQTTnet.Server Windows-x64
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime win-x64 --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\win-x64\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Windows-x64-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
####################################################################
Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
+6
View File
@@ -0,0 +1,6 @@
# Code of Conduct
This project has adopted the code of conduct defined by the Contributor Covenant
to clarify expected behavior in our community.
For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).
-2
View File
@@ -45,8 +45,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests",
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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}"
EndProject
Global
-21
View File
@@ -47,12 +47,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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{2BD01D53-4CA5-4142-BE8D-313876395E3E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Apps", "Apps", "{A56E3128-1639-4F31-873A-325E14BB6295}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Test.BlazorApp.Server", "Tests\MQTTnet.Test.BlazorApp\Server\MQTTnet.Test.BlazorApp.Server.csproj", "{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}"
@@ -217,22 +213,6 @@ 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
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|ARM.ActiveCfg = Debug|Any CPU
@@ -311,7 +291,6 @@ 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} = {A56E3128-1639-4F31-873A-325E14BB6295}
{2BD01D53-4CA5-4142-BE8D-313876395E3E} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
{61B165A0-5AA8-4E04-A53D-A22A84AA6EB7} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F} = {61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}
+1
View File
@@ -12,4 +12,5 @@
<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>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+3 -3
View File
@@ -5,10 +5,9 @@
</p>
[![NuGet Badge](https://buildstats.info/nuget/MQTTnet)](https://www.nuget.org/packages/MQTTnet)
[![Build status](https://ci.appveyor.com/api/projects/status/ycit86voxfevm2aa/branch/master?svg=true)](https://ci.appveyor.com/project/chkr1011/mqttnet)
[![BCH compliance](https://bettercodehub.com/edge/badge/chkr1011/MQTTnet?branch=master)](https://bettercodehub.com/)
[![Join the chat at https://gitter.im/MQTTnet/community](https://badges.gitter.im/MQTTnet/community.svg)](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://raw.githubusercontent.com/chkr1011/MQTTnet/master/LICENSE)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://raw.githubusercontent.com/chkr1011/MQTTnet/master/LICENSE)
# MQTTnet
@@ -56,7 +55,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
_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
* Runs under 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)
@@ -122,6 +121,7 @@ This library is used in the following projects:
* MQTTnet App (Cross platform client application for MQTT debugging, inspection etc., <https://github.com/chkr1011/MQTTnet.App>)
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
* SparkplugNet (Sparkplug library for .Net, <https://github.com/SeppPenner/SparkplugNet>)
* Silverback (Framework to build event-driven applications - support for MQTT, Kafka & RabbitMQ) <https://github.com/BEagle1984/silverback>
Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents.
@@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Implementations;
using MQTTnet.Server;
@@ -61,7 +62,7 @@ namespace MQTTnet.AspNetCore.Extensions
private static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
{
var logger = new MqttNetLogger();
var logger = new MqttNetEventLogger();
services.AddSingleton<IMqttNetLogger>(logger);
services.AddSingleton<MqttHostedServer>();
@@ -4,14 +4,14 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore
{
public class MqttHostedServer : MqttServer, IHostedService
public sealed class MqttHostedServer : MqttServer, IHostedService
{
private readonly IMqttServerOptions _options;
readonly IMqttServerOptions _options;
public MqttHostedServer(IMqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
: base(adapters, logger)
@@ -1,12 +1,12 @@
using Microsoft.AspNetCore.Http;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Server;
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.AspNetCore
{
@@ -1,4 +1,4 @@
using System;
using System;
using MQTTnet.Client.Options;
using MQTTnet.Server;
@@ -17,5 +17,11 @@ namespace MQTTnet.Extensions.ManagedClient
int MaxPendingMessages { get; }
MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; }
/// <summary>
/// Defines the maximum amount of topic filters which will be sent in a SUBSCRIBE/UNSUBSCRIBE packet.
/// Amazon AWS limits this number to 8. The default is int.MaxValue.
/// </summary>
int MaxTopicFiltersInSubscribeUnsubscribePackets { get; }
}
}
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
@@ -8,6 +8,9 @@
<Company />
<Authors />
<PackageId />
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
@@ -1,4 +1,4 @@
using MQTTnet.Client;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Publishing;
@@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -28,11 +29,12 @@ namespace MQTTnet.Extensions.ManagedClient
/// at reconnect and are solely owned by <see cref="MaintainConnectionAsync"/>.
/// </summary>
readonly Dictionary<string, MqttQualityOfServiceLevel> _reconnectSubscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
readonly HashSet<string> _unsubscriptions = new HashSet<string>();
readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
readonly IMqttNetScopedLogger _logger;
readonly MqttNetSourceLogger _logger;
readonly AsyncLock _messageQueueLock = new AsyncLock();
@@ -47,7 +49,7 @@ namespace MQTTnet.Extensions.ManagedClient
InternalClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateScopedLogger(nameof(ManagedMqttClient));
_logger = logger.WithSource(nameof(ManagedMqttClient));
}
public bool IsConnected => InternalClient.IsConnected;
@@ -205,7 +207,6 @@ namespace MQTTnet.Extensions.ManagedClient
await applicationMessageSkippedHandler.HandleApplicationMessageSkippedAsync(applicationMessageSkippedEventArgs).ConfigureAwait(false);
}
}
}
}
@@ -215,6 +216,11 @@ namespace MQTTnet.Extensions.ManagedClient
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
foreach (var topicFilter in topicFilters)
{
MqttTopicValidator.ThrowIfInvalidSubscribe(topicFilter.Topic);
}
lock (_subscriptions)
{
foreach (var topicFilter in topicFilters)
@@ -243,6 +249,7 @@ namespace MQTTnet.Extensions.ManagedClient
_unsubscriptions.Add(topic);
}
}
_subscriptionsQueuedSignal.Release();
return Task.FromResult(0);
@@ -476,8 +483,14 @@ namespace MQTTnet.Extensions.ManagedClient
lock (_subscriptions)
{
subscriptions = _subscriptions.Select(i => new MqttTopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList();
subscriptions = _subscriptions.Select(i => new MqttTopicFilter
{
Topic = i.Key,
QualityOfServiceLevel = i.Value
}).ToList();
_subscriptions.Clear();
unsubscriptions = new HashSet<string>(_unsubscriptions);
_unsubscriptions.Clear();
}
@@ -487,7 +500,7 @@ namespace MQTTnet.Extensions.ManagedClient
continue;
}
_logger.Verbose("Publishing {0} subscriptions and {1} unsubscriptions)", subscriptions.Count, unsubscriptions.Count);
_logger.Verbose("Publishing {0} added and {1} removed subscriptions", subscriptions.Count, unsubscriptions.Count);
foreach (var unsubscription in unsubscriptions)
{
@@ -499,22 +512,53 @@ namespace MQTTnet.Extensions.ManagedClient
_reconnectSubscriptions[subscription.Topic] = subscription.QualityOfServiceLevel;
}
try
var addedTopicFilters = new List<MqttTopicFilter>();
foreach (var subscription in subscriptions)
{
if (unsubscriptions.Any())
addedTopicFilters.Add(subscription);
if (addedTopicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
{
await InternalClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false);
await SendSubscribeUnsubscribe(addedTopicFilters, null).ConfigureAwait(false);
addedTopicFilters.Clear();
}
}
if (subscriptions.Any())
await SendSubscribeUnsubscribe(addedTopicFilters, null).ConfigureAwait(false);
var removedTopicFilters = new List<string>();
foreach (var unSub in unsubscriptions)
{
removedTopicFilters.Add(unSub);
if (removedTopicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
{
await InternalClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
await SendSubscribeUnsubscribe(null, removedTopicFilters).ConfigureAwait(false);
removedTopicFilters.Clear();
}
}
catch (Exception exception)
await SendSubscribeUnsubscribe(null, removedTopicFilters).ConfigureAwait(false);
}
}
async Task SendSubscribeUnsubscribe(List<MqttTopicFilter> addedSubscriptions, List<string> removedSubscriptions)
{
try
{
if (removedSubscriptions != null && removedSubscriptions.Any())
{
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
await InternalClient.UnsubscribeAsync(removedSubscriptions.ToArray()).ConfigureAwait(false);
}
if (addedSubscriptions != null && addedSubscriptions.Any())
{
await InternalClient.SubscribeAsync(addedSubscriptions.ToArray()).ConfigureAwait(false);
}
}
catch (Exception exception)
{
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
}
}
@@ -526,8 +570,26 @@ namespace MQTTnet.Extensions.ManagedClient
{
if (_reconnectSubscriptions.Any())
{
var subscriptions = _reconnectSubscriptions.Select(i => new MqttTopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value });
await InternalClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
var subscriptions = _reconnectSubscriptions.Select(i => new MqttTopicFilter
{
Topic = i.Key,
QualityOfServiceLevel = i.Value
});
var topicFilters = new List<MqttTopicFilter>();
foreach (var sub in subscriptions)
{
topicFilters.Add(sub);
if (topicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
{
await SendSubscribeUnsubscribe(topicFilters, null).ConfigureAwait(false);
topicFilters.Clear();
}
}
await SendSubscribeUnsubscribe(topicFilters, null).ConfigureAwait(false);
}
}
catch (Exception exception)
@@ -617,4 +679,4 @@ namespace MQTTnet.Extensions.ManagedClient
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
}
}
}
}
@@ -1,10 +1,10 @@
using System;
using System;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
{
public class ManagedMqttClientOptions : IManagedMqttClientOptions
public sealed class ManagedMqttClientOptions : IManagedMqttClientOptions
{
public IMqttClientOptions ClientOptions { get; set; }
@@ -17,5 +17,7 @@ namespace MQTTnet.Extensions.ManagedClient
public int MaxPendingMessages { get; set; } = int.MaxValue;
public MqttPendingMessagesOverflowStrategy PendingMessagesOverflowStrategy { get; set; } = MqttPendingMessagesOverflowStrategy.DropNewMessage;
public int MaxTopicFiltersInSubscribeUnsubscribePackets { get; set; } = int.MaxValue;
}
}
@@ -1,4 +1,4 @@
using System;
using System;
using MQTTnet.Client.Options;
using MQTTnet.Server;
@@ -68,7 +68,13 @@ namespace MQTTnet.Extensions.ManagedClient
options(_clientOptionsBuilder);
return this;
}
public ManagedMqttClientOptionsBuilder WithMaxTopicFiltersInSubscribeUnsubscribePackets(int value)
{
_options.MaxTopicFiltersInSubscribeUnsubscribePackets = value;
return this;
}
public ManagedMqttClientOptions Build()
{
if (_clientOptionsBuilder != null)
@@ -1,5 +1,5 @@
using MQTTnet.Diagnostics;
using System;
using System;
using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -7,12 +7,8 @@ namespace MQTTnet.Extensions.Rpc
{
public interface IMqttRpcClient : IDisposable
{
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel);
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken);
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel);
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken);
Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken);
}
}
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
@@ -8,6 +8,9 @@
<Company />
<Authors />
<PackageId />
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
+50 -51
View File
@@ -1,13 +1,14 @@
using MQTTnet.Client;
using MQTTnet.Client;
using MQTTnet.Exceptions;
using MQTTnet.Extensions.Rpc.Options;
using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
using MQTTnet.Protocol;
using System;
using System.Collections.Concurrent;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client.Subscribing;
using MQTTnet.Implementations;
namespace MQTTnet.Extensions.Rpc
{
@@ -35,22 +36,27 @@ namespace MQTTnet.Extensions.Rpc
_mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler;
}
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
return ExecuteAsync(timeout, methodName, Encoding.UTF8.GetBytes(payload), qualityOfServiceLevel, CancellationToken.None);
using (var timeoutToken = new CancellationTokenSource(timeout))
{
try
{
return await ExecuteAsync(methodName, payload, qualityOfServiceLevel, timeoutToken.Token).ConfigureAwait(false);
}
catch (OperationCanceledException exception)
{
if (timeoutToken.IsCancellationRequested)
{
throw new MqttCommunicationTimedOutException(exception);
}
throw;
}
}
}
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken)
{
return ExecuteAsync(timeout, methodName, Encoding.UTF8.GetBytes(payload), qualityOfServiceLevel, cancellationToken);
}
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
return ExecuteAsync(timeout, methodName, payload, qualityOfServiceLevel, CancellationToken.None);
}
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken)
public async Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken)
{
if (methodName == null) throw new ArgumentNullException(nameof(methodName));
@@ -84,66 +90,59 @@ namespace MQTTnet.Extensions.Rpc
.WithTopic(requestTopic)
.WithPayload(payload)
.WithQualityOfServiceLevel(qualityOfServiceLevel)
.WithResponseTopic(responseTopic)
.Build();
try
{
var tcs = new TaskCompletionSource<byte[]>();
if (!_waitingCalls.TryAdd(responseTopic, tcs))
#if NET452
var awaitable = new TaskCompletionSource<byte[]>();
#else
var awaitable = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
#endif
if (!_waitingCalls.TryAdd(responseTopic, awaitable))
{
throw new InvalidOperationException();
}
await _mqttClient.SubscribeAsync(responseTopic, qualityOfServiceLevel).ConfigureAwait(false);
await _mqttClient.PublishAsync(requestMessage).ConfigureAwait(false);
var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
.WithTopicFilter(responseTopic, qualityOfServiceLevel)
.Build();
using (var timeoutCts = new CancellationTokenSource(timeout))
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token))
await _mqttClient.SubscribeAsync(subscribeOptions, cancellationToken).ConfigureAwait(false);
await _mqttClient.PublishAsync(requestMessage, cancellationToken).ConfigureAwait(false);
using (cancellationToken.Register(() => { awaitable.TrySetCanceled(); }))
{
linkedCts.Token.Register(() =>
{
if (!tcs.Task.IsCompleted && !tcs.Task.IsFaulted && !tcs.Task.IsCanceled)
{
tcs.TrySetCanceled();
}
});
try
{
var result = await tcs.Task.ConfigureAwait(false);
timeoutCts.Cancel(false);
return result;
}
catch (OperationCanceledException exception)
{
if (timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
{
throw new MqttCommunicationTimedOutException(exception);
}
else
{
throw;
}
}
return await awaitable.Task.ConfigureAwait(false);
}
}
finally
{
_waitingCalls.TryRemove(responseTopic, out _);
await _mqttClient.UnsubscribeAsync(responseTopic).ConfigureAwait(false);
}
}
Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var tcs))
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var awaitable))
{
return Task.FromResult(0);
return PlatformAbstractionLayer.CompletedTask;
}
tcs.TrySetResult(eventArgs.ApplicationMessage.Payload);
#if NET452
Task.Run(() => awaitable.TrySetResult(eventArgs.ApplicationMessage.Payload));
#else
awaitable.TrySetResult(eventArgs.ApplicationMessage.Payload);
#endif
return Task.FromResult(0);
// Set this message to handled to that other code can avoid execution etc.
eventArgs.IsHandled = true;
return PlatformAbstractionLayer.CompletedTask;
}
public void Dispose()
@@ -158,4 +157,4 @@ namespace MQTTnet.Extensions.Rpc
_waitingCalls.Clear();
}
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Text;
using System.Threading.Tasks;
using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc
{
public static class MqttRpcClientExtensions
{
public static Task<byte[]> ExecuteAsync(this IMqttRpcClient client, TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (client == null) throw new ArgumentNullException(nameof(client));
var buffer = Encoding.UTF8.GetBytes(payload ?? string.Empty);
return client.ExecuteAsync(timeout, methodName, buffer, qualityOfServiceLevel);
}
}
}
@@ -18,14 +18,15 @@ namespace MQTTnet.Extensions.Rpc
public IMqttApplicationMessageReceivedHandler OriginalHandler { get; }
public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{
// First try to check if there is a pending RPC call!
await _handleReceivedApplicationMessageAsync(eventArgs).ConfigureAwait(false);
if (OriginalHandler != null)
{
return OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs);
await OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
}
return _handleReceivedApplicationMessageAsync(eventArgs);
}
}
}
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
@@ -8,6 +8,9 @@
<Company />
<Authors />
<PackageId />
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
@@ -4,6 +4,7 @@ using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using System;
using MQTTnet.Diagnostics.Logger;
namespace MQTTnet.Extensions.WebSocket4Net
{
@@ -1,12 +0,0 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "3.1.4",
"commands": [
"dotnet-ef"
]
}
}
}
@@ -1,35 +0,0 @@
using System.IO;
namespace MQTTnet.Server.Configuration
{
public class CertificateSettingsModel
{
/// <summary>
/// Path to certificate.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Password of certificate.
/// </summary>
public string Password { get; set; }
/// <summary>
/// Read certificate file.
/// </summary>
public byte[] ReadCertificate()
{
if (string.IsNullOrEmpty(Path) || string.IsNullOrWhiteSpace(Path))
{
throw new FileNotFoundException("No path set");
}
if (!File.Exists(Path))
{
throw new FileNotFoundException($"Could not find Certificate in path: {Path}");
}
return File.ReadAllBytes(Path);
}
}
}
@@ -1,53 +0,0 @@
namespace MQTTnet.Server.Configuration
{
/// <summary>
/// MQTT settings Model
/// </summary>
public class MqttSettingsModel
{
/// <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;
}
}
@@ -1,24 +0,0 @@
using System;
using System.IO;
namespace MQTTnet.Server.Configuration
{
public static class PathHelper
{
public static string ExpandPath(string path)
{
if (path == null)
{
return null;
}
var uri = new Uri(path, UriKind.RelativeOrAbsolute);
if (!uri.IsAbsoluteUri)
{
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
}
return path;
}
}
}
@@ -1,11 +0,0 @@
namespace MQTTnet.Server.Configuration
{
public class RetainedApplicationMessagesModel
{
public bool Persist { get; set; } = false;
public int WriteInterval { get; set; } = 10;
public string Path { get; set; }
}
}
@@ -1,11 +0,0 @@
using System.Collections.Generic;
namespace MQTTnet.Server.Configuration
{
public class ScriptingSettingsModel
{
public string ScriptsPath { get; set; }
public List<string> IncludePaths { get; set; }
}
}
@@ -1,102 +0,0 @@
using System.IO;
using System.Net;
namespace MQTTnet.Server.Configuration
{
/// <summary>
/// Listen Entry Settings Model
/// </summary>
public class TcpEndPointModel
{
/// <summary>
/// Certificate settings.
/// </summary>
public CertificateSettingsModel Certificate { 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 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}");
}
}
}
@@ -1,18 +0,0 @@
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; }
}
}
@@ -1,79 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MQTTnet.Server.Mqtt;
using MQTTnet.Server.Status;
namespace MQTTnet.Server.Controllers
{
[Authorize]
[ApiController]
public class ClientsController : Controller
{
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);
}
[Route("api/v1/clients/{clientId}/statistics")]
[HttpDelete]
public async Task<ActionResult> DeleteClientStatistics(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);
}
client.ResetStatistics();
return StatusCode((int)HttpStatusCode.NoContent);
}
}
}
@@ -1,51 +0,0 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MQTTnet.Protocol;
using MQTTnet.Server.Mqtt;
namespace MQTTnet.Server.Controllers
{
[Authorize]
[ApiController]
public class MessagesController : Controller
{
private readonly MqttServerService _mqttServerService;
public MessagesController(MqttServerService mqttServerService)
{
_mqttServerService = mqttServerService ?? throw new ArgumentNullException(nameof(mqttServerService));
}
[Route("api/v1/messages")]
[HttpPost]
public async Task<ActionResult> PostMessage(MqttApplicationMessage message)
{
await _mqttServerService.PublishAsync(message);
return Ok();
}
[Route("api/v1/messages/{*topic}")]
[HttpPost]
public async Task<ActionResult> PostMessage(string topic, int qosLevel = 0)
{
byte[] payload;
using (var memoryStream = new MemoryStream())
{
await HttpContext.Request.Body.CopyToAsync(memoryStream);
payload = memoryStream.ToArray();
}
var message = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel((MqttQualityOfServiceLevel)qosLevel)
.Build();
return await PostMessage(message);
}
}
}
@@ -1,64 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MQTTnet.Server.Mqtt;
namespace MQTTnet.Server.Controllers
{
[Authorize]
[ApiController]
public class RetainedApplicationMessagesController : Controller
{
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);
}
}
}
@@ -1,65 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MQTTnet.Server.Scripting;
using MQTTnet.Server.Web;
namespace MQTTnet.Server.Controllers
{
[Authorize]
[ApiController]
public class ScriptsController : Controller
{
private readonly PythonScriptHostService _pythonScriptHostService;
public ScriptsController(PythonScriptHostService pythonScriptHostService)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
}
[Route("api/v1/scripts")]
[HttpGet]
public ActionResult<List<string>> GetScriptUids()
{
return _pythonScriptHostService.GetScriptUids();
}
[Route("api/v1/scripts/uid")]
[HttpGet]
public async Task<ActionResult<string>> GetScript(string uid)
{
var script = await _pythonScriptHostService.ReadScriptAsync(uid, HttpContext.RequestAborted);
if (script == null)
{
return NotFound();
}
return Content(script);
}
[Route("api/v1/scripts/uid")]
[HttpPost]
public Task PostScript(string uid)
{
var code = HttpContext.Request.ReadBodyAsString();
return _pythonScriptHostService.WriteScriptAsync(uid, code, CancellationToken.None);
}
[Route("api/v1/scripts/uid")]
[HttpDelete]
public Task DeleteScript(string uid)
{
return _pythonScriptHostService.DeleteScriptAsync(uid);
}
[Route("api/v1/scripts/initialize")]
[HttpPost]
public Task PostInitializeScripts()
{
return _pythonScriptHostService.TryInitializeScriptsAsync();
}
}
}
@@ -1,20 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.Reflection;
namespace MQTTnet.Server.Controllers
{
[Authorize]
[ApiController]
public class ServerController : Controller
{
[Route("api/v1/server/version")]
[HttpGet]
public ActionResult<string> GetVersion()
{
var fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
return fileVersion.ProductVersion.ToString();
}
}
}
@@ -1,79 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MQTTnet.Server.Mqtt;
using MQTTnet.Server.Status;
namespace MQTTnet.Server.Controllers
{
[Authorize]
[ApiController]
public class SessionsController : Controller
{
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);
}
}
}
-97
View File
@@ -1,97 +0,0 @@
# MQTTnet
MIT License
Copyright (c) 2019 MQTTnet Team
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.
# Swashbuckle
Copyright (c) 2013, Richard Morris
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
# IronPython
Copyright (c) .NET Foundation and Contributors
All Rights Reserved
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
@@ -1,43 +0,0 @@
//using MQTTnet.Diagnostics;
//using System;
//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 IMqttNetLogger 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);
// }
// }
//}
@@ -1,64 +0,0 @@
using Microsoft.Extensions.Logging;
using MQTTnet.Diagnostics;
using System;
using System.Threading;
namespace MQTTnet.Server.Logging
{
public sealed class MqttNetLoggerWrapper : IMqttNetLogger
{
readonly ILogger<MqttServer> _logger;
public MqttNetLoggerWrapper(ILogger<MqttServer> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
public IMqttNetScopedLogger CreateScopedLogger(string source)
{
return new MqttNetScopedLogger(this, source);
}
public void Publish(MqttNetLogLevel level, string source, string message, object[] parameters, Exception exception)
{
var convertedLogLevel = ConvertLogLevel(level);
_logger.Log(convertedLogLevel, exception, message, parameters);
var logMessagePublishedEvent = LogMessagePublished;
if (logMessagePublishedEvent != null)
{
var logMessage = new MqttNetLogMessage
{
Timestamp = DateTime.UtcNow,
ThreadId = Thread.CurrentThread.ManagedThreadId,
Source = source,
Level = level,
Message = message,
Exception = exception
};
logMessagePublishedEvent.Invoke(this, new MqttNetLogMessagePublishedEventArgs(logMessage));
}
}
public void Publish(MqttNetLogLevel logLevel, string message, object[] parameters, Exception exception)
{
Publish(logLevel, null, message, parameters, exception);
}
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;
}
}
}
@@ -1,93 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net5.0</TargetFrameworks>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<AssemblyName>MQTTnet.Server</AssemblyName>
<RootNamespace>MQTTnet.Server</RootNamespace>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<Company />
<Product>MQTTnet</Product>
<Description>Standalone MQTT server with Python scripting backend.</Description>
<Authors>The authors of MQTTnet.Server.</Authors>
<PackageId />
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<LangVersion>latest</LangVersion>
<Version>3.0.11</Version>
<TypeScriptToolsVersion>3.3</TypeScriptToolsVersion>
</PropertyGroup>
<PropertyGroup>
<StartupObject>MQTTnet.Server.Program</StartupObject>
<UserSecretsId>c564f0de-28b4-45bf-b726-4d665d705653</UserSecretsId>
<Copyright>(c) Christian Kratky 2016-2020</Copyright>
<PackageProjectUrl>https://github.com/chkr1011/MQTTnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/chkr1011/MQTTnet</RepositoryUrl>
</PropertyGroup>
<PropertyGroup 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.11" />
<PackageReference Include="IronPython.StdLib" Version="2.7.11" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.6.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.6.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net5.0'">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.5" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.0" />
</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="README.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Scripts\README.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Scripts\00_sample.py">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Web\authorization_handler.py">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Web\wwwroot\Hello.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Web\wwwroot\mqtt_Test.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Web\wwwroot\pahoJS_Test.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\PublishProfiles\" />
</ItemGroup>
</Project>
@@ -1,48 +0,0 @@
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(MqttSettingsModel 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);
}
}
}
@@ -1,54 +0,0 @@
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
{
// This might be not set when a message was published by the server instead of a client.
context.SessionItems.TryGetValue(MqttServerConnectionValidator.WrappedSessionItemsKey, out var sessionItems);
var pythonContext = new PythonDictionary
{
{ "client_id", context.ClientId },
{ "session_items", sessionItems },
{ "retain", context.ApplicationMessage.Retain },
{ "accept_publish", context.AcceptPublish },
{ "close_connection", context.CloseConnection },
{ "topic", context.ApplicationMessage.Topic },
{ "qos", (int)context.ApplicationMessage.QualityOfServiceLevel }
};
_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;
}
}
}
@@ -1,39 +0,0 @@
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;
}
}
}
@@ -1,40 +0,0 @@
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;
}
}
}
@@ -1,41 +0,0 @@
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;
}
}
}
@@ -1,40 +0,0 @@
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;
}
}
}
@@ -1,61 +0,0 @@
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 MqttServerConnectionValidator : IMqttServerConnectionValidator
{
public const string WrappedSessionItemsKey = "WRAPPED_ITEMS";
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger _logger;
public MqttServerConnectionValidator(PythonScriptHostService pythonScriptHostService, ILogger<MqttServerConnectionValidator> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task ValidateConnectionAsync(MqttConnectionValidatorContext context)
{
try
{
var sessionItems = new PythonDictionary();
var pythonContext = new PythonDictionary
{
{ "endpoint", context.Endpoint },
{ "is_secure_connection", context.IsSecureConnection },
{ "client_id", context.ClientId },
{ "username", context.Username },
{ "password", context.Password },
{ "raw_password", new Bytes(context.RawPassword ?? new byte[0]) },
{ "clean_session", context.CleanSession},
{ "authentication_method", context.AuthenticationMethod},
{ "authentication_data", new Bytes(context.AuthenticationData ?? new byte[0]) },
{ "session_items", sessionItems },
{ "result", PythonConvert.Pythonfy(context.ReasonCode) }
};
_pythonScriptHostService.InvokeOptionalFunction("on_validate_client_connection", pythonContext);
context.ReasonCode = PythonConvert.ParseEnum<MqttConnectReasonCode>((string)pythonContext["result"]);
context.SessionItems[WrappedSessionItemsKey] = sessionItems;
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while validating client connection.");
context.ReasonCode = MqttConnectReasonCode.UnspecifiedError;
}
return Task.CompletedTask;
}
}
}
@@ -1,273 +0,0 @@
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;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
namespace MQTTnet.Server.Mqtt
{
public class MqttServerService
{
readonly ILogger<MqttServerService> _logger;
readonly MqttSettingsModel _settings;
readonly MqttApplicationMessageInterceptor _mqttApplicationMessageInterceptor;
readonly MqttServerStorage _mqttServerStorage;
readonly MqttClientConnectedHandler _mqttClientConnectedHandler;
readonly MqttClientDisconnectedHandler _mqttClientDisconnectedHandler;
readonly MqttClientSubscribedTopicHandler _mqttClientSubscribedTopicHandler;
readonly MqttClientUnsubscribedTopicHandler _mqttClientUnsubscribedTopicHandler;
readonly MqttServerConnectionValidator _mqttConnectionValidator;
readonly IMqttServer _mqttServer;
readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
readonly MqttUnsubscriptionInterceptor _mqttUnsubscriptionInterceptor;
readonly PythonScriptHostService _pythonScriptHostService;
readonly MqttWebSocketServerAdapter _webSocketServerAdapter;
public MqttServerService(
MqttSettingsModel mqttSettings,
CustomMqttFactory mqttFactory,
MqttClientConnectedHandler mqttClientConnectedHandler,
MqttClientDisconnectedHandler mqttClientDisconnectedHandler,
MqttClientSubscribedTopicHandler mqttClientSubscribedTopicHandler,
MqttClientUnsubscribedTopicHandler mqttClientUnsubscribedTopicHandler,
MqttServerConnectionValidator mqttConnectionValidator,
MqttSubscriptionInterceptor mqttSubscriptionInterceptor,
MqttUnsubscriptionInterceptor mqttUnsubscriptionInterceptor,
MqttApplicationMessageInterceptor mqttApplicationMessageInterceptor,
MqttServerStorage mqttServerStorage,
PythonScriptHostService pythonScriptHostService,
ILogger<MqttServerService> logger)
{
_settings = mqttSettings ?? throw new ArgumentNullException(nameof(mqttSettings));
_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));
_mqttUnsubscriptionInterceptor = mqttUnsubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttUnsubscriptionInterceptor));
_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);
var adapters = new List<IMqttServerAdapter>
{
new MqttTcpServerAdapter(mqttFactory.Logger)
{
TreatSocketOpeningErrorAsWarning = true // Opening other ports than for HTTP is not allows in Azure App Services.
},
_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);
}
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.");
}
}
IMqttServerOptions CreateMqttServerOptions()
{
var options = new MqttServerOptionsBuilder()
.WithMaxPendingMessagesPerClient(_settings.MaxPendingMessagesPerClient)
.WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(_settings.CommunicationTimeout))
.WithConnectionValidator(_mqttConnectionValidator)
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
.WithUnsubscriptionInterceptor(_mqttUnsubscriptionInterceptor)
.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)
{
#if NETCOREAPP3_1 || NET5_0
options
.WithEncryptedEndpoint()
.WithEncryptionSslProtocol(SslProtocols.Tls13);
#else
options
.WithEncryptedEndpoint()
.WithEncryptionSslProtocol(SslProtocols.Tls12);
#endif
if (!string.IsNullOrEmpty(_settings.EncryptedTcpEndPoint?.Certificate?.Path))
{
IMqttServerCertificateCredentials certificateCredentials = null;
if (!string.IsNullOrEmpty(_settings.EncryptedTcpEndPoint?.Certificate?.Password))
{
certificateCredentials = new MqttServerCertificateCredentials
{
Password = _settings.EncryptedTcpEndPoint.Certificate.Password
};
}
options.WithEncryptionCertificate(_settings.EncryptedTcpEndPoint.Certificate.ReadCertificate(), certificateCredentials);
}
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();
}
}
}
@@ -1,119 +0,0 @@
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 MqttSettingsModel _mqttSettings;
private readonly ILogger<MqttServerStorage> _logger;
private string _path;
private bool _messagesHaveChanged;
public MqttServerStorage(MqttSettingsModel mqttSettings, ILogger<MqttServerStorage> logger)
{
_mqttSettings = mqttSettings ?? throw new ArgumentNullException(nameof(mqttSettings));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void Configure()
{
if (_mqttSettings.RetainedApplicationMessages?.Persist != true ||
string.IsNullOrEmpty(_mqttSettings.RetainedApplicationMessages.Path))
{
_logger.LogInformation("Persisting of retained application messages is disabled.");
return;
}
_path = PathHelper.ExpandPath(_mqttSettings.RetainedApplicationMessages.Path);
// 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(_mqttSettings.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(_path, 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 (_mqttSettings.RetainedApplicationMessages?.Persist != true)
{
return null;
}
if (!File.Exists(_path))
{
return null;
}
try
{
var json = await File.ReadAllTextAsync(_path).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;
}
}
}
}
@@ -1,49 +0,0 @@
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 sessionItems = (PythonDictionary)context.SessionItems[MqttServerConnectionValidator.WrappedSessionItemsKey];
var pythonContext = new PythonDictionary
{
{ "client_id", context.ClientId },
{ "session_items", sessionItems },
{ "topic", context.TopicFilter.Topic },
{ "qos", (int)context.TopicFilter.QualityOfServiceLevel },
{ "accept_subscription", context.AcceptSubscription },
{ "close_connection", context.CloseConnection }
};
_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;
}
}
}
@@ -1,48 +0,0 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttUnsubscriptionInterceptor : IMqttServerUnsubscriptionInterceptor
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger _logger;
public MqttUnsubscriptionInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttUnsubscriptionInterceptor> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task InterceptUnsubscriptionAsync(MqttUnsubscriptionInterceptorContext context)
{
try
{
var sessionItems = (PythonDictionary)context.SessionItems[MqttServerConnectionValidator.WrappedSessionItemsKey];
var pythonContext = new PythonDictionary
{
{ "client_id", context.ClientId },
{ "session_items", sessionItems },
{ "topic", context.Topic },
{ "accept_unsubscription", context.AcceptUnsubscription },
{ "close_connection", context.CloseConnection }
};
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_unsubscription", pythonContext);
context.AcceptUnsubscription = (bool)pythonContext["accept_unsubscription"];
context.CloseConnection = (bool)pythonContext["close_connection"];
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while intercepting unsubscription.");
}
return Task.CompletedTask;
}
}
}
-80
View File
@@ -1,80 +0,0 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using MQTTnet.Server.Web;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
namespace MQTTnet.Server
{
public static class Program
{
public static int Main(string[] args)
{
try
{
PrintLogo();
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(serverOptions =>
{
})
.UseWebRoot(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Web", "wwwroot"))
.UseStartup<Startup>();
}).Build().Run();
return 0;
}
catch (Exception exception)
{
Console.WriteLine(exception);
return -1;
}
}
static void PrintLogo()
{
Console.ResetColor();
Console.ForegroundColor = ConsoleColor.Red;
const string LogoText =
@"
███╗ ███╗ ██████╗ ████████╗████████╗███╗ ██╗███████╗████████╗ ███████╗███████╗██████╗ ██╗ ██╗███████╗██████╗
████╗ ████║██╔═══██╗╚══██╔══╝╚══██╔══╝████╗ ██║██╔════╝╚══██╔══╝ ██╔════╝██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗
██╔████╔██║██║ ██║ ██║ ██║ ██╔██╗ ██║█████╗ ██║ ███████╗█████╗ ██████╔╝██║ ██║█████╗ ██████╔╝
██║╚██╔╝██║██║▄▄ ██║ ██║ ██║ ██║╚██╗██║██╔══╝ ██║ ╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██╔══╝ ██╔══██╗
██║ ╚═╝ ██║╚██████╔╝ ██║ ██║ ██║ ╚████║███████╗ ██║ ███████║███████╗██║ ██║ ╚████╔╝ ███████╗██║ ██║
╚═╝ ╚═╝ ╚══▀▀═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝
";
Console.WriteLine(LogoText);
Console.ResetColor();
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("The official MQTT server implementation of MQTTnet");
Console.WriteLine("Copyright (c) 2017-2020 The MQTTnet Team");
Console.WriteLine(@"https://github.com/chkr1011/MQTTnet");
Console.ForegroundColor = ConsoleColor.White;
var fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
Console.WriteLine($@"
Version: {fileVersion.ProductVersion}
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.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.ResetColor();
Console.WriteLine();
}
}
}
-10
View File
@@ -1,10 +0,0 @@
# Starting portable version
The portable version requires a local installation of the .net core runtime. With that runtime installed the server can be started via the following comand.
dotnet .\MQTTnet.Server.dll
# Starting self contained versions
The self contained versions are fully portable versions including the .net core runtime. The server can be started using the contained executable files.
Windows: MQTTnet.Server.exe
Linux: MQTTnet.Server (must be set to _executable_ first)
@@ -1,47 +0,0 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
namespace MQTTnet.Server.Scripting.DataSharing
{
public class DataSharingService
{
readonly Dictionary<string, object> _storage = new Dictionary<string, object>();
readonly PythonScriptHostService _pythonScriptHostService;
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;
}
}
}
}
@@ -1,88 +0,0 @@
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);
}
}
}
@@ -1,66 +0,0 @@
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Text;
namespace MQTTnet.Server.Scripting
{
public class PythonIOStream : Stream
{
readonly ILogger _logger;
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();
}
}
}
@@ -1,203 +0,0 @@
using Microsoft.Extensions.Logging;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using MQTTnet.Server.Configuration;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Server.Scripting
{
public class PythonScriptHostService
{
readonly IDictionary<string, object> _proxyObjects = new ExpandoObject();
readonly List<PythonScriptInstance> _scriptInstances = new List<PythonScriptInstance>();
readonly string _scriptsPath;
readonly ScriptingSettingsModel _scriptingSettings;
readonly ILogger<PythonScriptHostService> _logger;
readonly ScriptEngine _scriptEngine;
public PythonScriptHostService(ScriptingSettingsModel scriptingSettings, PythonIOStream pythonIOStream, ILogger<PythonScriptHostService> logger)
{
_scriptingSettings = scriptingSettings ?? throw new ArgumentNullException(nameof(scriptingSettings));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_scriptEngine = IronPython.Hosting.Python.CreateEngine();
_scriptEngine.Runtime.IO.SetOutput(pythonIOStream, Encoding.UTF8);
_scriptsPath = PathHelper.ExpandPath(scriptingSettings.ScriptsPath);
}
public void Configure()
{
AddSearchPaths(_scriptEngine);
TryInitializeScriptsAsync().GetAwaiter().GetResult();
}
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.Uid}'.");
}
}
}
}
public List<string> GetScriptUids()
{
lock (_scriptInstances)
{
return _scriptInstances.Select(si => si.Uid).ToList();
}
}
public Task<string> ReadScriptAsync(string uid, CancellationToken cancellationToken)
{
if (uid == null) throw new ArgumentNullException(nameof(uid));
string path;
lock (_scriptInstances)
{
path = _scriptInstances.FirstOrDefault(si => si.Uid == uid)?.Path;
}
if (path == null || !File.Exists(path))
{
return null;
}
return File.ReadAllTextAsync(path, Encoding.UTF8, cancellationToken);
}
public async Task WriteScriptAsync(string uid, string code, CancellationToken cancellationToken)
{
var path = Path.Combine(_scriptsPath, uid + ".py");
await File.WriteAllTextAsync(path, code, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
await TryInitializeScriptsAsync().ConfigureAwait(false);
}
public async Task DeleteScriptAsync(string uid)
{
var path = Path.Combine(_scriptsPath, uid + ".py");
if (File.Exists(path))
{
File.Delete(path);
await TryInitializeScriptsAsync().ConfigureAwait(false);
}
}
public async Task TryInitializeScriptsAsync()
{
lock (_scriptInstances)
{
foreach (var scriptInstance in _scriptInstances)
{
try
{
scriptInstance.InvokeOptionalFunction("destroy");
}
catch (Exception exception)
{
_logger.LogWarning(exception, $"Error while unloading script '{scriptInstance.Uid}'.");
}
}
_scriptInstances.Clear();
}
foreach (var path in Directory.GetFiles(_scriptsPath, "*.py", SearchOption.AllDirectories).OrderBy(file => file))
{
await TryInitializeScriptAsync(path).ConfigureAwait(false);
}
}
async Task TryInitializeScriptAsync(string path)
{
var uid = new FileInfo(path).Name.Replace(".py", string.Empty, StringComparison.OrdinalIgnoreCase);
try
{
_logger.LogTrace($"Initializing Python script '{uid}'...");
var code = await File.ReadAllTextAsync(path).ConfigureAwait(false);
var scriptInstance = CreateScriptInstance(uid, path, code);
scriptInstance.InvokeOptionalFunction("initialize");
lock (_scriptInstances)
{
_scriptInstances.Add(scriptInstance);
}
_logger.LogInformation($"Initialized script '{uid}'.");
}
catch (Exception exception)
{
_logger.LogError(exception, $"Error while initializing script '{uid}'.");
}
}
PythonScriptInstance CreateScriptInstance(string uid, string path, 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(uid, path, scriptScope);
}
void AddSearchPaths(ScriptEngine scriptEngine)
{
if (_scriptingSettings.IncludePaths?.Any() != true)
{
return;
}
var searchPaths = scriptEngine.GetSearchPaths();
foreach (var path in _scriptingSettings.IncludePaths)
{
var effectivePath = PathHelper.ExpandPath(path);
if (Directory.Exists(effectivePath))
{
searchPaths.Add(effectivePath);
_logger.LogInformation($"Added Python lib path: {effectivePath}");
}
}
scriptEngine.SetSearchPaths(searchPaths);
}
}
}
@@ -1,60 +0,0 @@
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
using System;
namespace MQTTnet.Server.Scripting
{
public class PythonScriptInstance
{
readonly ScriptScope _scriptScope;
public PythonScriptInstance(string uid, string path, ScriptScope scriptScope)
{
Uid = uid;
Path = path;
_scriptScope = scriptScope;
}
public string Uid { get; }
public string Path { 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);
}
}
}
}
}
-140
View File
@@ -1,140 +0,0 @@
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 destroy():
"""
This function is invoked when the script is unloaded due to a script file update etc.
"""
print("Bye 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)
# Access some custom data here which was inserted upon connect and may use it for validation.
mqtt_net_server.write_shared_data(context["client_id"], {"custom_value_1": 1, "custom_value_2": True})
return
# Supported results:
# * success
# * unspecified_error
# * malformed_packet
# * protocol_error
# * implementation_specific_error
# * unsupported_protocol_version
# * client_identifier_not_valid
# * bad_user_name_or_password
# * not_authorized
# * server_unavailable
# * server_busy
# * banned
# * bad_authentication_method
# * topic_name_invalid
# * packet_too_large
# * quota_exceeded
# * payload_format_invalid
# * retain_not_supported
# * qos_not_supported
# * use_another_server
# * server_moved
# * connection_rate_exceeded
if context["client_id"] != "test_client":
context["result"] = "bad_user_name_or_password"
return
if context["username"] != "bud spencer":
context["result"] = "bad_user_name_or_password"
return
if context["password_string"] != "secret":
context["result"] = "bad_user_name_or_password"
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"]))
-21
View File
@@ -1,21 +0,0 @@
# MQTT Scripts
This directory contains scripts which are loaded by the server and can be used to perform the following tasks.
* Validation of client connections via credentials, client IDs etc.
* Manipulation of every processed message.
* Validation of subscription attempts.
* 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.
@@ -1,118 +0,0 @@
using System;
using System.IO;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Microsoft.Scripting;
namespace MQTTnet.Server.Web
{
public class AuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly ILogger<AuthenticationHandler> _logger;
public AuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory loggerFactory,
UrlEncoder encoder,
ISystemClock clock,
ILogger<AuthenticationHandler> logger)
: base(options, loggerFactory, encoder, clock)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey(HeaderNames.Authorization))
{
return AuthenticateResult.NoResult();
}
try
{
var headerValue = Request.Headers[HeaderNames.Authorization];
var parsedHeaderValue = AuthenticationHeaderValue.Parse(Request.Headers[HeaderNames.Authorization]);
var scheme = parsedHeaderValue.Scheme;
var parameter = parsedHeaderValue.Parameter;
string username = null;
string password = null;
if (scheme.Equals("Basic", StringComparison.OrdinalIgnoreCase))
{
var credentialBytes = Convert.FromBase64String(parsedHeaderValue.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
username = credentials[0];
password = credentials[1];
}
var context = new PythonDictionary
{
["header_value"] = headerValue,
["scheme"] = scheme,
["parameter"] = parameter,
["username"] = username,
["password"] = password,
["is_authenticated"] = false
};
if (!ValidateUser(context))
{
return AuthenticateResult.Fail("Invalid credentials.");
}
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, context.get("username") as string ?? string.Empty)
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
catch (Exception exception)
{
_logger.LogWarning("Error while authenticating user.", exception);
return AuthenticateResult.Fail(exception);
}
finally
{
await Task.CompletedTask;
}
}
private bool ValidateUser(PythonDictionary context)
{
var handlerPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Web", "authorization_handler.py");
if (!File.Exists(handlerPath))
{
return false;
}
var code = File.ReadAllText(handlerPath);
var scriptEngine = IronPython.Hosting.Python.CreateEngine();
//scriptEngine.Runtime.IO.SetOutput(new PythonIOStream(_logger.), Encoding.UTF8);
var scriptScope = scriptEngine.CreateScope();
var scriptSource = scriptScope.Engine.CreateScriptSourceFromString(code, SourceCodeKind.File);
var compiledCode = scriptSource.Compile();
compiledCode.Execute(scriptScope);
var function = scriptScope.Engine.Operations.GetMember<PythonFunction>(scriptScope, "handle_authenticate");
scriptScope.Engine.Operations.Invoke(function, context);
return context.get("is_authenticated", false) as bool? == true;
}
}
}
-20
View File
@@ -1,20 +0,0 @@
using System;
using System.IO;
using System.Text;
using Microsoft.AspNetCore.Http;
namespace MQTTnet.Server.Web
{
public static class Extensions
{
public static string ReadBodyAsString(this HttpRequest request)
{
if (request == null) throw new ArgumentNullException(nameof(request));
using (var reader = new StreamReader(request.Body, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
}
-228
View File
@@ -1,228 +0,0 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;
using Microsoft.OpenApi.Models;
using Microsoft.Scripting.Utils;
using MQTTnet.AspNetCore;
using MQTTnet.Server.Configuration;
using MQTTnet.Server.Logging;
using MQTTnet.Server.Mqtt;
using MQTTnet.Server.Scripting;
using MQTTnet.Server.Scripting.DataSharing;
using Newtonsoft.Json.Converters;
using Swashbuckle.AspNetCore.SwaggerUI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace MQTTnet.Server.Web
{
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,
MqttServerService mqttServerService,
PythonScriptHostService pythonScriptHostService,
DataSharingService dataSharingService,
MqttSettingsModel mqttSettings)
{
application.UseDefaultFiles();
application.UseStaticFiles();
application.UseHsts();
application.UseRouting();
application.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
application.UseAuthentication();
application.UseAuthorization();
application.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
ConfigureWebSocketEndpoint(application, mqttServerService, mqttSettings);
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.AddCors();
services.AddControllers();
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddNewtonsoftJson(o =>
{
o.SerializerSettings.Converters.Add(new StringEnumConverter());
});
ReadMqttSettings(services);
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<MqttServerConnectionValidator>();
services.AddSingleton<MqttSubscriptionInterceptor>();
services.AddSingleton<MqttUnsubscriptionInterceptor>();
services.AddSingleton<MqttApplicationMessageInterceptor>();
services.AddSwaggerGen(c =>
{
var securityScheme = new OpenApiSecurityScheme
{
Scheme = "basic",
Name = HeaderNames.Authorization,
Type = SecuritySchemeType.Http,
In = ParameterLocation.Header,
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Swagger"
}
};
c.AddSecurityDefinition("Swagger", securityScheme);
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
[securityScheme] = new List<string>()
});
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")
},
});
});
services.AddAuthentication("Basic")
.AddScheme<AuthenticationSchemeOptions, AuthenticationHandler>("Basic", null)
.AddCookie();
}
void ReadMqttSettings(IServiceCollection services)
{
var mqttSettings = new MqttSettingsModel();
Configuration.Bind("MQTT", mqttSettings);
services.AddSingleton(mqttSettings);
var scriptingSettings = new ScriptingSettingsModel();
Configuration.Bind("Scripting", scriptingSettings);
services.AddSingleton(scriptingSettings);
}
static void ConfigureWebSocketEndpoint(
IApplicationBuilder application,
MqttServerService mqttServerService,
MqttSettingsModel mqttSettings)
{
if (mqttSettings?.WebSocketEndPoint?.Enabled != true)
{
return;
}
if (string.IsNullOrEmpty(mqttSettings.WebSocketEndPoint.Path))
{
return;
}
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromSeconds(mqttSettings.WebSocketEndPoint.KeepAliveInterval)
};
if (mqttSettings.WebSocketEndPoint.AllowedOrigins?.Any() == true)
{
webSocketOptions.AllowedOrigins.AddRange(mqttSettings.WebSocketEndPoint.AllowedOrigins);
}
application.UseWebSockets(webSocketOptions);
application.Use(async (context, next) =>
{
if (context.Request.Path == mqttSettings.WebSocketEndPoint.Path)
{
if (context.WebSockets.IsWebSocketRequest)
{
string subProtocol = null;
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
{
subProtocol = MqttSubProtocolSelector.SelectSubProtocol(requestedSubProtocolValues);
}
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol).ConfigureAwait(false))
{
await mqttServerService.RunWebSocketConnectionAsync(webSocket, context).ConfigureAwait(false);
}
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
}
else
{
await next().ConfigureAwait(false);
}
});
}
}
}
@@ -1,20 +0,0 @@
def handle_authenticate(context):
"""
This function is invoked whenever a user tries to access protected HTTP resources.
This function must exist and return a proper value. Otherwise the request is denied.
All authentication data must be sent via using the "Authorization" header. This header
will be parsed and all values will be exposed to the context.
"""
header_value = context["header_value"] # The untouched header value.
scheme = context["scheme"]
parameter = context["parameter"]
username = context["username"] # Only set if proper "Basic" authorization is used.
password = context["password"] # Only set if proper "Basic" authorization is used.
context["is_authenticated"] = True # Change this to _False_ in case of invalid credentials.
# Example for an API key with proper format.
if header_value == "APIKey 123456":
context["is_authenticated"] = True
@@ -1 +0,0 @@
World
@@ -1,33 +0,0 @@
<html>
<head>
<title>MQTT JS Example</title>
<script src="https://cdn.jsdelivr.net/npm/mqtt@4.0.1/dist/mqtt.min.js" type="text/javascript"></script>
<script type="text/javascript">
var client = mqtt.connect('ws://localhost:80/mqtt')
client.on('connect', function () {
client.subscribe('mqtt_subscription', function (err) {
console.log("Subscribed");
})
})
client.on('message', function (topic, message) {
console.log(topic);
console.log(message.toString())
})
function buttonClick() {
client.publish("MQTT_JS_TOPIC", "payload");
}
</script>
</head>
<body>
<button onclick="buttonClick()">Publish</button>
</body>
</html>
@@ -1,41 +0,0 @@
<html>
<head>
<title>paho JS Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript"></script>
<script type="text/javascript">
var mqttClient = new Paho.MQTT.Client("localhost", 80, "/mqtt", "pahoJS_client");
mqttClient.onConnectionLost = onConnectionLost;
mqttClient.onMessageArrived = onMessageArrived;
mqttClient.connect({ onSuccess: onConnect });
function onConnect() {
console.log("MQTT client connected");
mqttClient.subscribe("pahoJS_subscription");
}
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0) {
console.log("onConnectionLost:" + responseObject.errorMessage);
}
}
function onMessageArrived(message) {
console.log("onMessageArrived:" + message.payloadString);
}
function buttonClick() {
message = new Paho.MQTT.Message("Hello");
message.destinationName = "pahoJS_topic";
mqttClient.send(message);
}
</script>
</head>
<body>
<button onclick="buttonClick()">Publish</button>
</body>
</html>
@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
-65
View File
@@ -1,65 +0,0 @@
{
"Kestrel": {
"EndPoints": {
"Http": {
"Url": "http://*:80"
},
"Https": {
"Url": "http://*: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,
"Certificate": {
"Path": "/absolute/path/to/pfx",
"Password": ""
}
},
"WebSocketEndPoint": {
"Enabled": true,
"Path": "/mqtt",
"KeepAliveInterval": 120, // In seconds.
"ReceiveBufferSize": 4096,
"AllowedOrigins": [] // List of strings with URLs.
},
"CommunicationTimeout": 15, // In seconds.
"ConnectionBacklog": 10, // Set 0 to disable
"EnablePersistentSessions": true,
"MaxPendingMessagesPerClient": 250,
"RetainedApplicationMessages": {
"Persist": true,
"Path": "RetainedApplicationMessages.json",
"WriteInterval": 10 // In seconds.
},
"EnableDebugLogging": false
},
"Scripting": {
"ScriptsPath": "Scripts",
"IncludePaths": []
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"AllowedHosts": "*"
}
+3 -2
View File
@@ -11,6 +11,7 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Adapter
@@ -24,7 +25,7 @@ namespace MQTTnet.Adapter
readonly byte[] _fixedHeaderBuffer = new byte[2];
readonly MqttPacketInspectorHandler _packetInspectorHandler;
readonly IMqttNetScopedLogger _logger;
readonly MqttNetSourceLogger _logger;
readonly IMqttChannel _channel;
readonly AsyncLock _syncRoot = new AsyncLock();
@@ -40,7 +41,7 @@ namespace MQTTnet.Adapter
_packetInspectorHandler = new MqttPacketInspectorHandler(packetInspector, logger);
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateScopedLogger(nameof(MqttChannelAdapter));
_logger = logger.WithSource(nameof(MqttChannelAdapter));
}
public string Endpoint => _channel.Endpoint;
@@ -1,17 +1,19 @@
using MQTTnet.Client.Connecting;
using System;
using MQTTnet.Client.Connecting;
using MQTTnet.Exceptions;
namespace MQTTnet.Adapter
{
public class MqttConnectingFailedException : MqttCommunicationException
public sealed class MqttConnectingFailedException : MqttCommunicationException
{
public MqttConnectingFailedException(MqttClientAuthenticateResult result)
: base($"Connecting with MQTT server failed ({result.ResultCode.ToString()}).")
public MqttConnectingFailedException(string message, Exception innerException, MqttClientConnectResult connectResult)
: base(message, innerException)
{
Result = result;
Result = connectResult;
}
public MqttClientAuthenticateResult Result { get; }
public MqttClientConnectResultCode ResultCode => Result.ResultCode;
public MqttClientConnectResult Result { get; }
public MqttClientConnectResultCode ResultCode => Result?.ResultCode ?? MqttClientConnectResultCode.UnspecifiedError;
}
}
@@ -2,6 +2,7 @@ using System;
using System.IO;
using System.Linq;
using MQTTnet.Diagnostics;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Adapter
@@ -10,7 +11,7 @@ namespace MQTTnet.Adapter
{
readonly MemoryStream _receivedPacketBuffer;
readonly IMqttPacketInspector _packetInspector;
readonly IMqttNetScopedLogger _logger;
readonly MqttNetSourceLogger _logger;
public MqttPacketInspectorHandler(IMqttPacketInspector packetInspector, IMqttNetLogger logger)
{
@@ -22,7 +23,7 @@ namespace MQTTnet.Adapter
}
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateScopedLogger(nameof(MqttPacketInspectorHandler));
_logger = logger.WithSource(nameof(MqttPacketInspectorHandler));
}
public void BeginReceivePacket()
@@ -1,83 +0,0 @@
using System.Collections.Generic;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Client.Connecting
{
// TODO: Consider renaming this to _MqttClientConnectResult_
public class MqttClientAuthenticateResult
{
/// <summary>
/// Gets or sets the result code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientConnectResultCode ResultCode { get; set; }
public bool IsSessionPresent { get; set; }
public bool? WildcardSubscriptionAvailable { get; set; }
public bool? RetainAvailable { get; set; }
public string AssignedClientIdentifier { get; set; }
/// <summary>
/// Gets or sets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; set; }
/// <summary>
/// Gets or sets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; set; }
public uint? MaximumPacketSize { get; set; }
/// <summary>
/// Gets or sets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; set; }
public ushort? ReceiveMaximum { get; set; }
public MqttQualityOfServiceLevel MaximumQoS { get; set; }
/// <summary>
/// Gets or sets the response information.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ResponseInformation { get; set; }
public ushort? TopicAliasMaximum { get; set; }
public string ServerReference { get; set; }
public ushort? ServerKeepAlive { get; set; }
public uint? SessionExpiryInterval { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the subscription identifiers are available or not.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? SubscriptionIdentifiersAvailable { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the shared subscriptions are available or not.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? SharedSubscriptionAvailable { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; }
}
}
@@ -0,0 +1,115 @@
using System.Collections.Generic;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Client.Connecting
{
public sealed class MqttClientConnectResult
{
/// <summary>
/// Gets the result code.
/// MQTTv5 only.
/// </summary>
public MqttClientConnectResultCode ResultCode { get; internal set; }
/// <summary>
/// Gets a value indicating whether a session was already available or not.
/// MQTTv5 only.
/// </summary>
public bool IsSessionPresent { get; internal set; }
/// <summary>
/// Gets a value indicating whether wildcards can be used in subscriptions at the current server.
/// MQTTv5 only.
/// </summary>
public bool WildcardSubscriptionAvailable { get; internal set; }
/// <summary>
/// Gets whether the server supports retained messages.
/// MQTTv5 only.
/// </summary>
public bool RetainAvailable { get; internal set; }
/// <summary>
/// Gets the client identifier which was chosen by the server.
/// MQTTv5 only.
/// </summary>
public string AssignedClientIdentifier { get; internal set; }
/// <summary>
/// Gets the authentication method.
/// MQTTv5 only.
/// </summary>
public string AuthenticationMethod { get; internal set; }
/// <summary>
/// Gets the authentication data.
/// MQTTv5 only.
/// </summary>
public byte[] AuthenticationData { get; internal set; }
public uint? MaximumPacketSize { get; internal set; }
/// <summary>
/// Gets the reason string.
/// MQTTv5 only.
/// </summary>
public string ReasonString { get; internal set; }
public ushort? ReceiveMaximum { get; internal set; }
/// <summary>
/// Gets the maximum QoS which is supported by the server.
/// MQTTv5 only.
/// </summary>
public MqttQualityOfServiceLevel MaximumQoS { get; internal set; }
/// <summary>
/// Gets the response information.
/// MQTTv5 only.
/// </summary>
public string ResponseInformation { get; internal set; }
/// <summary>
/// Gets the maximum value for a topic alias. 0 means not supported.
/// MQTTv5 only.
/// </summary>
public ushort TopicAliasMaximum { get; internal set; }
/// <summary>
/// Gets an alternate server which should be used instead of the current one.
/// MQTTv5 only.
/// </summary>
public string ServerReference { get; internal set; }
/// <summary>
/// Gets the keep alive interval which was chosen by the server instead of the
/// keep alive interval from the client CONNECT packet.
/// MQTTv5 only.
/// </summary>
public ushort? ServerKeepAlive { get; internal set; }
public uint? SessionExpiryInterval { get; internal set; }
/// <summary>
/// Gets a value indicating whether the subscription identifiers are available or not.
/// MQTTv5 only.
/// </summary>
public bool SubscriptionIdentifiersAvailable { get; internal set; }
/// <summary>
/// Gets a value indicating whether the shared subscriptions are available or not.
/// MQTTv5 only.
/// </summary>
public bool SharedSubscriptionAvailable { get; internal set; }
/// <summary>
/// Gets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// MQTTv5 only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; internal set; }
}
}
@@ -4,15 +4,15 @@ namespace MQTTnet.Client.Connecting
{
public class MqttClientConnectedEventArgs : EventArgs
{
public MqttClientConnectedEventArgs(MqttClientAuthenticateResult authenticateResult)
public MqttClientConnectedEventArgs(MqttClientConnectResult connectResult)
{
AuthenticateResult = authenticateResult ?? throw new ArgumentNullException(nameof(authenticateResult));
ConnectResult = connectResult ?? throw new ArgumentNullException(nameof(connectResult));
}
/// <summary>
/// Gets the authentication result.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientAuthenticateResult AuthenticateResult { get; }
public MqttClientConnectResult ConnectResult { get; }
}
}
@@ -12,7 +12,7 @@
ServerBusy = 137,
ServerShuttingDown = 139,
BadAuthenticationMethod = 140,
KeepaliveTimeout = 141,
KeepAliveTimeout = 141,
SessionTakenOver = 142,
TopicFilterInvalid = 143,
TopicNameInvalid = 144,
@@ -5,11 +5,11 @@ namespace MQTTnet.Client.Disconnecting
{
public sealed class MqttClientDisconnectedEventArgs : EventArgs
{
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientAuthenticateResult authenticateResult, MqttClientDisconnectReason reason)
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientConnectResult connectResult, MqttClientDisconnectReason reason)
{
ClientWasConnected = clientWasConnected;
Exception = exception;
AuthenticateResult = authenticateResult;
ConnectResult = connectResult;
Reason = reason;
ReasonCode = reason;
@@ -23,7 +23,7 @@ namespace MQTTnet.Client.Disconnecting
/// Gets the authentication result.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientAuthenticateResult AuthenticateResult { get; }
public MqttClientConnectResult ConnectResult { get; }
/// <summary>
/// Gets or sets the reason.
+1 -1
View File
@@ -28,7 +28,7 @@ namespace MQTTnet.Client
/// </summary>
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
Task<MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken);
Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken);
Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken);
@@ -1,5 +1,6 @@
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.LowLevelClient;
namespace MQTTnet.Client
+189 -161
View File
@@ -14,9 +14,9 @@ using MQTTnet.PacketDispatcher;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Implementations;
namespace MQTTnet.Client
@@ -25,12 +25,10 @@ namespace MQTTnet.Client
{
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
readonly Stopwatch _sendTracker = new Stopwatch();
readonly Stopwatch _receiveTracker = new Stopwatch();
readonly object _disconnectLock = new object();
readonly IMqttClientAdapterFactory _adapterFactory;
readonly IMqttNetScopedLogger _logger;
readonly MqttNetSourceLogger _logger;
CancellationTokenSource _backgroundCancellationTokenSource;
Task _packetReceiverTask;
@@ -41,16 +39,18 @@ namespace MQTTnet.Client
IMqttChannelAdapter _adapter;
bool _cleanDisconnectInitiated;
long _isDisconnectPending;
bool _isConnected;
volatile int _connectionStatus;
MqttClientDisconnectReason _disconnectReason;
DateTime _lastPacketSentTimestamp;
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
_adapterFactory = channelFactory ?? throw new ArgumentNullException(nameof(channelFactory));
_logger = logger.CreateScopedLogger(nameof(MqttClient));
_logger = logger.WithSource(nameof(MqttClient));
}
public IMqttClientConnectedHandler ConnectedHandler { get; set; }
@@ -59,11 +59,11 @@ namespace MQTTnet.Client
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; }
public bool IsConnected => _isConnected && Interlocked.Read(ref _isDisconnectPending) == 0;
public bool IsConnected => (MqttClientConnectionStatus)_connectionStatus == MqttClientConnectionStatus.Connected;
public IMqttClientOptions Options { get; private set; }
public async Task<MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken)
public async Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options.ChannelOptions == null) throw new ArgumentException("ChannelOptions are not set.");
@@ -72,19 +72,23 @@ namespace MQTTnet.Client
ThrowIfDisposed();
MqttClientAuthenticateResult authenticateResult = null;
if (CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connecting, MqttClientConnectionStatus.Disconnected) != MqttClientConnectionStatus.Disconnected)
{
throw new InvalidOperationException("Not allowed to connect while connect/disconnect is pending.");
}
MqttClientConnectResult connectResult = null;
try
{
Options = options;
_packetIdentifierProvider.Reset();
_packetDispatcher.Cancel();
_packetDispatcher.CancelAll();
_backgroundCancellationTokenSource = new CancellationTokenSource();
var backgroundCancellationToken = _backgroundCancellationTokenSource.Token;
_isDisconnectPending = 0;
var adapter = _adapterFactory.CreateClientAdapter(options);
_adapter = adapter;
@@ -99,28 +103,27 @@ namespace MQTTnet.Client
_packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken);
authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false);
connectResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false);
}
_sendTracker.Restart();
_receiveTracker.Restart();
_lastPacketSentTimestamp = DateTime.UtcNow;
if (Options.KeepAlivePeriod != TimeSpan.Zero)
{
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken);
}
_isConnected = true;
CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connected, MqttClientConnectionStatus.Connecting);
_logger.Info("Connected.");
var connectedHandler = ConnectedHandler;
if (connectedHandler != null)
{
await connectedHandler.HandleConnectedAsync(new MqttClientConnectedEventArgs(authenticateResult)).ConfigureAwait(false);
await connectedHandler.HandleConnectedAsync(new MqttClientConnectedEventArgs(connectResult)).ConfigureAwait(false);
}
return authenticateResult;
return connectResult;
}
catch (Exception exception)
{
@@ -128,10 +131,7 @@ namespace MQTTnet.Client
_logger.Error(exception, "Error while connecting with server.");
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(null, exception, authenticateResult).ConfigureAwait(false);
}
await DisconnectInternalAsync(null, exception, connectResult).ConfigureAwait(false);
throw;
}
@@ -143,7 +143,8 @@ namespace MQTTnet.Client
ThrowIfDisposed();
if (DisconnectIsPending())
var clientWasConnected = IsConnected;
if (DisconnectIsPendingOrFinished())
{
return;
}
@@ -153,7 +154,7 @@ namespace MQTTnet.Client
_disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
_cleanDisconnectInitiated = true;
if (_isConnected)
if (clientWasConnected)
{
var disconnectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(options);
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
@@ -161,13 +162,13 @@ namespace MQTTnet.Client
}
finally
{
await DisconnectInternalAsync(null, null, null).ConfigureAwait(false);
await DisconnectCoreAsync(null, null, null, clientWasConnected).ConfigureAwait(false);
}
}
public Task PingAsync(CancellationToken cancellationToken)
{
return SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket(), cancellationToken);
return SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken);
}
public Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken)
@@ -194,6 +195,11 @@ namespace MQTTnet.Client
{
if (options == null) throw new ArgumentNullException(nameof(options));
foreach (var topicFilter in options.TopicFilters)
{
MqttTopicValidator.ThrowIfInvalidSubscribe(topicFilter.Topic);
}
ThrowIfDisposed();
ThrowIfNotConnected();
@@ -232,21 +238,21 @@ namespace MQTTnet.Client
switch (applicationMessage.QualityOfServiceLevel)
{
case MqttQualityOfServiceLevel.AtMostOnce:
{
return PublishAtMostOnce(publishPacket, cancellationToken);
}
{
return PublishAtMostOnce(publishPacket, cancellationToken);
}
case MqttQualityOfServiceLevel.AtLeastOnce:
{
return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
}
{
return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
}
case MqttQualityOfServiceLevel.ExactlyOnce:
{
return PublishExactlyOnceAsync(publishPacket, cancellationToken);
}
{
return PublishExactlyOnceAsync(publishPacket, cancellationToken);
}
default:
{
throw new NotSupportedException();
}
{
throw new NotSupportedException();
}
}
}
@@ -278,18 +284,27 @@ namespace MQTTnet.Client
base.Dispose(disposing);
}
async Task<MqttClientAuthenticateResult> AuthenticateAsync(IMqttChannelAdapter channelAdapter, MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
async Task<MqttClientConnectResult> AuthenticateAsync(IMqttChannelAdapter channelAdapter, MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
{
var connectPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket(
willApplicationMessage,
Options);
MqttClientConnectResult result;
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
var result = channelAdapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket);
try
{
var connectPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket(
willApplicationMessage,
Options);
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
result = channelAdapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket);
}
catch (Exception exception)
{
throw new MqttConnectingFailedException($"Error while authenticating. {exception.Message}", exception, null);
}
if (result.ResultCode != MqttClientConnectResultCode.Success)
{
throw new MqttConnectingFailedException(result);
throw new MqttConnectingFailedException($"Connecting with MQTT server failed ({result.ResultCode}).", null, result);
}
_logger.Verbose("Authenticated MQTT connection with server established.");
@@ -299,7 +314,7 @@ namespace MQTTnet.Client
void ThrowIfNotConnected()
{
if (!IsConnected || Interlocked.Read(ref _isDisconnectPending) == 1)
if (!IsConnected)
{
throw new MqttCommunicationException("The client is not connected.");
}
@@ -310,12 +325,21 @@ namespace MQTTnet.Client
if (IsConnected) throw new MqttProtocolViolationException(message);
}
async Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult)
Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientConnectResult connectResult)
{
var clientWasConnected = _isConnected;
var clientWasConnected = IsConnected;
if (!DisconnectIsPendingOrFinished())
{
return DisconnectCoreAsync(sender, exception, connectResult, clientWasConnected);
}
return PlatformAbstractionLayer.CompletedTask;
}
async Task DisconnectCoreAsync(Task sender, Exception exception, MqttClientConnectResult connectResult, bool clientWasConnected)
{
TryInitiateDisconnect();
_isConnected = false;
try
{
@@ -339,17 +363,16 @@ namespace MQTTnet.Client
var keepAliveTask = WaitForTaskAsync(_keepAlivePacketsSenderTask, sender);
await Task.WhenAll(receiverTask, publishPacketReceiverTask, keepAliveTask).ConfigureAwait(false);
_publishPacketReceiverQueue?.Dispose();
}
catch (Exception e)
catch (Exception innerException)
{
_logger.Warning(e, "Error while waiting for internal tasks.");
_logger.Warning(innerException, "Error while waiting for internal tasks.");
}
finally
{
Cleanup();
_cleanDisconnectInitiated = false;
CompareExchangeConnectionStatus(MqttClientConnectionStatus.Disconnected, MqttClientConnectionStatus.Disconnecting);
_logger.Info("Disconnected.");
@@ -358,7 +381,7 @@ namespace MQTTnet.Client
{
// This handler must be executed in a new thread because otherwise a dead lock may happen
// when trying to reconnect in that handler etc.
Task.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, authenticateResult, _disconnectReason))).RunInBackground(_logger);
Task.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, connectResult, _disconnectReason))).RunInBackground(_logger);
}
}
}
@@ -382,7 +405,7 @@ namespace MQTTnet.Client
{
cancellationToken.ThrowIfCancellationRequested();
_sendTracker.Restart();
_lastPacketSentTimestamp = DateTime.UtcNow;
return _adapter.SendPacketAsync(packet, cancellationToken);
}
@@ -391,23 +414,22 @@ namespace MQTTnet.Client
{
cancellationToken.ThrowIfCancellationRequested();
ushort identifier = 0;
if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier > 0)
ushort packetIdentifier = 0;
if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier)
{
identifier = packetWithIdentifier.PacketIdentifier;
packetIdentifier = packetWithIdentifier.PacketIdentifier;
}
using (var packetAwaiter = _packetDispatcher.AddAwaiter<TResponsePacket>(identifier))
using (var packetAwaiter = _packetDispatcher.AddAwaitable<TResponsePacket>(packetIdentifier))
{
try
{
_sendTracker.Restart();
await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false);
await SendAsync(requestPacket, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
catch (Exception exception)
{
_logger.Warning(e, "Error when sending request packet ({0}).", requestPacket.GetType().Name);
packetAwaiter.Cancel();
_logger.Warning(exception, "Error when sending request packet ({0}).", requestPacket.GetType().Name);
packetAwaiter.Fail(exception);
}
try
@@ -418,7 +440,7 @@ namespace MQTTnet.Client
{
if (exception is MqttCommunicationTimedOutException)
{
_logger.Warning(null, "Timeout while waiting for response packet ({0}).", typeof(TResponsePacket).Name);
_logger.Warning("Timeout while waiting for response packet ({0}).", typeof(TResponsePacket).Name);
}
throw;
@@ -433,22 +455,22 @@ namespace MQTTnet.Client
_logger.Verbose("Start sending keep alive packets.");
var keepAlivePeriod = Options.KeepAlivePeriod;
while (!cancellationToken.IsCancellationRequested)
{
// Values described here: [MQTT-3.1.2-24].
var waitTime = keepAlivePeriod - _sendTracker.Elapsed;
var timeWithoutPacketSent = DateTime.UtcNow - _lastPacketSentTimestamp;
if (waitTime <= TimeSpan.Zero)
if (timeWithoutPacketSent > keepAlivePeriod)
{
await SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken).ConfigureAwait(false);
await PingAsync(cancellationToken).ConfigureAwait(false);
}
// Wait a fixed time in all cases. Calculation of the remaining time is complicated
// due to some edge cases and was buggy in the past. Now we wait half a second because the
// due to some edge cases and was buggy in the past. Now we wait several ms because the
// min keep alive value is one second so that the server will wait 1.5 seconds for a PING
// packet.
await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken).ConfigureAwait(false);
await Task.Delay(250, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception exception)
@@ -471,10 +493,7 @@ namespace MQTTnet.Client
_logger.Error(exception, "Error exception while sending/receiving keep alive packets.");
}
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
}
await DisconnectInternalAsync(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
}
finally
{
@@ -499,10 +518,7 @@ namespace MQTTnet.Client
if (packet == null)
{
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
}
await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
return;
}
@@ -529,12 +545,9 @@ namespace MQTTnet.Client
_logger.Error(exception, "Error while receiving packets.");
}
_packetDispatcher.Dispatch(exception);
_packetDispatcher.FailAll(exception);
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
finally
{
@@ -546,8 +559,6 @@ namespace MQTTnet.Client
{
try
{
_receiveTracker.Restart();
if (packet is MqttPublishPacket publishPacket)
{
EnqueueReceivedPublishPacket(publishPacket);
@@ -560,10 +571,6 @@ namespace MQTTnet.Client
{
await ProcessReceivedPubRelPacket(pubRelPacket, cancellationToken).ConfigureAwait(false);
}
else if (packet is MqttPingReqPacket)
{
await SendAsync(MqttPingRespPacket.Instance, cancellationToken).ConfigureAwait(false);
}
else if (packet is MqttDisconnectPacket disconnectPacket)
{
await ProcessReceivedDisconnectPacket(disconnectPacket).ConfigureAwait(false);
@@ -572,9 +579,20 @@ namespace MQTTnet.Client
{
await ProcessReceivedAuthPacket(authPacket).ConfigureAwait(false);
}
else if (packet is MqttPingRespPacket)
{
_packetDispatcher.TryDispatch(packet);
}
else if (packet is MqttPingReqPacket)
{
throw new MqttProtocolViolationException("The PINGREQ Packet is sent from a Client to the Server only.");
}
else
{
_packetDispatcher.Dispatch(packet);
if (!_packetDispatcher.TryDispatch(packet))
{
throw new MqttProtocolViolationException($"Received packet '{packet}' at an unexpected time.");
}
}
}
catch (Exception exception)
@@ -596,12 +614,9 @@ namespace MQTTnet.Client
_logger.Error(exception, "Error while receiving packets.");
}
_packetDispatcher.Dispatch(exception);
_packetDispatcher.FailAll(exception);
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
}
}
@@ -630,40 +645,11 @@ namespace MQTTnet.Client
}
var publishPacket = publishPacketDequeueResult.Item;
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
if (eventArgs.AutoAcknowledge)
{
await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (!eventArgs.ProcessingFailed)
{
await SendAsync(new MqttPubAckPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = (MqttPubAckReasonCode)eventArgs.ReasonCode
}, cancellationToken).ConfigureAwait(false);
}
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (!eventArgs.ProcessingFailed)
{
await SendAsync(new MqttPubRecPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = (MqttPubRecReasonCode)eventArgs.ReasonCode
}, cancellationToken).ConfigureAwait(false);
}
}
else
{
throw new MqttProtocolViolationException("Received a not supported QoS level.");
await eventArgs.AcknowledgeAsync(cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
@@ -676,17 +662,44 @@ namespace MQTTnet.Client
}
}
Task AcknowledgeReceivedPublishPacket(MqttApplicationMessageReceivedEventArgs eventArgs, CancellationToken cancellationToken)
{
if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{
// no response required
}
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
if (!eventArgs.ProcessingFailed)
{
var pubAckPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubAckPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
return SendAsync(pubAckPacket, cancellationToken);
}
}
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
if (!eventArgs.ProcessingFailed)
{
var pubRecPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRecPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
return SendAsync(pubRecPacket, cancellationToken);
}
}
else
{
throw new MqttProtocolViolationException("Received a not supported QoS level.");
}
return PlatformAbstractionLayer.CompletedTask;
}
Task ProcessReceivedPubRecPacket(MqttPubRecPacket pubRecPacket, CancellationToken cancellationToken)
{
if (!_packetDispatcher.TryDispatch(pubRecPacket))
{
// The packet is unknown. Probably due to a restart of the client.
// So wen send this to the server to trigger a full resend of the message.
return SendAsync(new MqttPubRelPacket
{
PacketIdentifier = pubRecPacket.PacketIdentifier,
ReasonCode = MqttPubRelReasonCode.PacketIdentifierNotFound
}, cancellationToken);
var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound);
return SendAsync(pubRelPacket, cancellationToken);
}
return PlatformAbstractionLayer.CompletedTask;
@@ -694,26 +707,18 @@ namespace MQTTnet.Client
Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
{
return SendAsync(new MqttPubCompPacket
{
PacketIdentifier = pubRelPacket.PacketIdentifier,
ReasonCode = MqttPubCompReasonCode.Success
}, cancellationToken);
var pubCompPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubCompPacket(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
return SendAsync(pubCompPacket, cancellationToken);
}
Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket)
{
_disconnectReason = (MqttClientDisconnectReason)(disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection);
_disconnectReason = (MqttClientDisconnectReason) (disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection);
// Also dispatch disconnect to waiting threads to generate a proper exception.
_packetDispatcher.Dispatch(disconnectPacket);
_packetDispatcher.FailAll(new MqttUnexpectedDisconnectReceivedException(disconnectPacket));
if (!DisconnectIsPending())
{
return DisconnectInternalAsync(_packetReceiverTask, null, null);
}
return PlatformAbstractionLayer.CompletedTask;
return DisconnectInternalAsync(_packetReceiverTask, null, null);
}
Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket)
@@ -731,14 +736,16 @@ namespace MQTTnet.Client
{
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(null);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(null);
}
async Task<MqttClientPublishResult> PublishAtLeastOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
var response = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(response);
var pubAckPacket = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubAckPacket);
}
async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
@@ -747,21 +754,17 @@ namespace MQTTnet.Client
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
var pubRelPacket = new MqttPubRelPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRelReasonCode.Success
};
var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(pubRecPacket, pubCompPacket);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubRecPacket, pubCompPacket);
}
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
{
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage);
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage, publishPacket, AcknowledgeReceivedPublishPacket);
var handler = ApplicationMessageReceivedHandler;
if (handler != null)
@@ -803,11 +806,36 @@ namespace MQTTnet.Client
}
}
bool DisconnectIsPending()
bool DisconnectIsPendingOrFinished()
{
// This will read the _isDisconnectPending and set it to "1" afterwards regardless of the value.
// So the first caller will get a "false" and all subsequent ones will get "true".
return Interlocked.CompareExchange(ref _isDisconnectPending, 1, 0) != 0;
var connectionStatus = (MqttClientConnectionStatus)_connectionStatus;
do
{
switch (connectionStatus)
{
case MqttClientConnectionStatus.Disconnected:
case MqttClientConnectionStatus.Disconnecting:
return true;
case MqttClientConnectionStatus.Connected:
case MqttClientConnectionStatus.Connecting:
// This will compare the _connectionStatus to old value and set it to "MqttClientConnectionStatus.Disconnecting" afterwards.
// So the first caller will get a "false" and all subsequent ones will get "true".
var curStatus = CompareExchangeConnectionStatus(MqttClientConnectionStatus.Disconnecting, connectionStatus);
if (curStatus == connectionStatus)
{
return false;
}
connectionStatus = curStatus;
break;
}
} while (true);
}
MqttClientConnectionStatus CompareExchangeConnectionStatus(MqttClientConnectionStatus value, MqttClientConnectionStatus comparand)
{
return (MqttClientConnectionStatus)Interlocked.CompareExchange(ref _connectionStatus, (int)value, (int)comparand);
}
}
}
}
@@ -0,0 +1,10 @@
namespace MQTTnet.Client
{
public enum MqttClientConnectionStatus
{
Disconnected,
Disconnecting,
Connected,
Connecting
}
}
+15 -3
View File
@@ -1,4 +1,4 @@
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.ExtendedAuthenticationExchange;
using MQTTnet.Client.Options;
@@ -112,7 +112,7 @@ namespace MQTTnet.Client
return client;
}
public static Task ReconnectAsync(this IMqttClient client)
public static Task<MqttClientConnectResult> ReconnectAsync(this IMqttClient client)
{
if (client == null) throw new ArgumentNullException(nameof(client));
@@ -124,6 +124,18 @@ namespace MQTTnet.Client
return client.ConnectAsync(client.Options);
}
public static Task<MqttClientConnectResult> ReconnectAsync(this IMqttClient client, CancellationToken cancellationToken)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (client.Options == null)
{
throw new InvalidOperationException("_ReconnectAsync_ can be used only if _ConnectAsync_ was called before.");
}
return client.ConnectAsync(client.Options, cancellationToken);
}
public static Task DisconnectAsync(this IMqttClient client)
{
if (client == null) throw new ArgumentNullException(nameof(client));
@@ -183,7 +195,7 @@ namespace MQTTnet.Client
return client.UnsubscribeAsync(options);
}
public static Task<MqttClientAuthenticateResult> ConnectAsync(this IMqttClient client, IMqttClientOptions options)
public static Task<MqttClientConnectResult> ConnectAsync(this IMqttClient client, IMqttClientOptions options)
{
if (client == null) throw new ArgumentNullException(nameof(client));
@@ -133,7 +133,7 @@ namespace MQTTnet.Client.Options
return this;
}
public MqttClientOptionsBuilder WithCredentials(string username, string password = null)
public MqttClientOptionsBuilder WithCredentials(string username, string password)
{
byte[] passwordBuffer = null;
@@ -145,7 +145,7 @@ namespace MQTTnet.Client.Options
return WithCredentials(username, passwordBuffer);
}
public MqttClientOptionsBuilder WithCredentials(string username, byte[] password = null)
public MqttClientOptionsBuilder WithCredentials(string username, byte[] password)
{
_options.Credentials = new MqttClientCredentials
{
@@ -155,6 +155,16 @@ namespace MQTTnet.Client.Options
return this;
}
public MqttClientOptionsBuilder WithCredentials(string username)
{
_options.Credentials = new MqttClientCredentials
{
Username = username
};
return this;
}
public MqttClientOptionsBuilder WithCredentials(IMqttClientCredentials credentials)
{
@@ -252,7 +262,11 @@ namespace MQTTnet.Client.Options
{
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
_tlsParameters = new MqttClientOptionsBuilderTlsParameters();
_tlsParameters = new MqttClientOptionsBuilderTlsParameters
{
UseTls = true
};
optionsBuilder(_tlsParameters);
return this;
}
@@ -287,7 +301,7 @@ namespace MQTTnet.Client.Options
#pragma warning disable CS0618 // Type or member is obsolete
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
#pragma warning restore CS0618 // Type or member is obsolete
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
ApplicationProtocols = _tlsParameters.ApplicationProtocols,
#endif
CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Authentication;
@@ -19,11 +19,7 @@ namespace MQTTnet.Client.Options
public Func<MqttClientCertificateValidationCallbackContext, bool> CertificateValidationHandler { get; set; }
#if NETCOREAPP3_1 || NET5_0
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls13;
#else
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
#endif
public SslProtocols SslProtocol { get; set; } = SslProtocols.None;
#if WINDOWS_UWP
public IEnumerable<IEnumerable<byte>> Certificates { get; set; }
@@ -31,9 +27,9 @@ namespace MQTTnet.Client.Options
public IEnumerable<X509Certificate> Certificates { get; set; }
#endif
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
public List<SslApplicationProtocol> ApplicationProtocols { get;set; }
#endif
#endif
public bool AllowUntrustedCertificates { get; set; }
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Authentication;
@@ -22,15 +22,11 @@ namespace MQTTnet.Client.Options
public List<X509Certificate> Certificates { get; set; }
#endif
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
#endif
#if NETCOREAPP3_1 || NET5_0
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls13;
#else
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
#endif
public SslProtocols SslProtocol { get; set; } = SslProtocols.None;
[Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; }
@@ -1,11 +1,12 @@
using System;
using System.Threading.Tasks;
using MQTTnet.Implementations;
namespace MQTTnet.Client.Receiving
{
public class MqttApplicationMessageReceivedHandlerDelegate : IMqttApplicationMessageReceivedHandler
public sealed class MqttApplicationMessageReceivedHandlerDelegate : IMqttApplicationMessageReceivedHandler
{
private readonly Func<MqttApplicationMessageReceivedEventArgs, Task> _handler;
readonly Func<MqttApplicationMessageReceivedEventArgs, Task> _handler;
public MqttApplicationMessageReceivedHandlerDelegate(Action<MqttApplicationMessageReceivedEventArgs> handler)
{
@@ -14,7 +15,7 @@ namespace MQTTnet.Client.Receiving
_handler = context =>
{
handler(context);
return Task.FromResult(0);
return PlatformAbstractionLayer.CompletedTask;
};
}
@@ -18,7 +18,7 @@ namespace MQTTnet.Client.Subscribing
/// The broker will return the subscription identifier associated with this PUBLISH packet and the PUBLISH packet to the client when need to forward PUBLISH packets matching this subscription to this client.
/// Hint: MQTT 5 feature only.
/// </summary>
public uint? SubscriptionIdentifier { get; set; }
public uint SubscriptionIdentifier { get; set; }
/// <summary>
/// Gets or sets the user properties.
@@ -2,12 +2,13 @@
using MQTTnet.Protocol;
using System;
using System.Collections.Generic;
using MQTTnet.Exceptions;
namespace MQTTnet.Client.Subscribing
{
public class MqttClientSubscribeOptionsBuilder
public sealed class MqttClientSubscribeOptionsBuilder
{
private readonly MqttClientSubscribeOptions _subscribeOptions = new MqttClientSubscribeOptions();
readonly MqttClientSubscribeOptions _subscribeOptions = new MqttClientSubscribeOptions();
/// <summary>
/// Adds the user property to the subscribe options.
@@ -31,8 +32,13 @@ namespace MQTTnet.Client.Subscribing
return this;
}
public MqttClientSubscribeOptionsBuilder WithSubscriptionIdentifier(uint? subscriptionIdentifier)
public MqttClientSubscribeOptionsBuilder WithSubscriptionIdentifier(uint subscriptionIdentifier)
{
if (subscriptionIdentifier == 0)
{
throw new MqttProtocolViolationException("Subscription identifier cannot be 0.");
}
_subscribeOptions.SubscriptionIdentifier = subscriptionIdentifier;
return this;
}
@@ -40,9 +46,9 @@ namespace MQTTnet.Client.Subscribing
public MqttClientSubscribeOptionsBuilder WithTopicFilter(
string topic,
MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
bool? noLocal = null,
bool? retainAsPublished = null,
MqttRetainHandling? retainHandling = null)
bool noLocal = false,
bool retainAsPublished = false,
MqttRetainHandling retainHandling = MqttRetainHandling.SendAtSubscribe)
{
return WithTopicFilter(new MqttTopicFilter
{
@@ -1,11 +0,0 @@
using System;
namespace MQTTnet.Diagnostics
{
public interface IMqttNetScopedLogger
{
IMqttNetScopedLogger CreateScopedLogger(string source);
void Publish(MqttNetLogLevel logLevel, string message, object[] parameters, Exception exception);
}
}
@@ -1,11 +1,11 @@
using System;
namespace MQTTnet.Diagnostics
namespace MQTTnet.Diagnostics.Logger
{
public interface IMqttNetLogger
{
IMqttNetScopedLogger CreateScopedLogger(string source);
bool IsEnabled { get; }
void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception);
}
}
@@ -0,0 +1,60 @@
using System;
namespace MQTTnet.Diagnostics.Logger
{
/// <summary>
/// This logger fires an event when a new message was published.
/// </summary>
public sealed class MqttNetEventLogger : IMqttNetLogger
{
public MqttNetEventLogger(string logId = null)
{
LogId = logId;
}
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
public string LogId { get; }
public bool IsEnabled { get; set; } = true;
public void Publish(MqttNetLogLevel level, string source, string message, object[] parameters, Exception exception)
{
var eventHandler = LogMessagePublished;
if (eventHandler == null)
{
// No listener is attached so we can step out.
// Keep a reference to the handler because the handler
// might be null after preparing the message.
return;
}
if (parameters?.Length > 0 && message?.Length > 0)
{
try
{
message = string.Format(message, parameters);
}
catch (FormatException)
{
message = "MESSAGE FORMAT INVALID: " + message;
}
}
// We only use UTC here to improve performance. Using a local date time
// would require to load the time zone settings!
var logMessage = new MqttNetLogMessage
{
LogId = LogId,
Timestamp = DateTime.UtcNow,
Source = source,
ThreadId = Environment.CurrentManagedThreadId,
Level = level,
Message = message,
Exception = exception
};
eventHandler.Invoke(this, new MqttNetLogMessagePublishedEventArgs(logMessage));
}
}
}
@@ -1,4 +1,4 @@
namespace MQTTnet.Diagnostics
namespace MQTTnet.Diagnostics.Logger
{
public enum MqttNetLogLevel
{
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Diagnostics
namespace MQTTnet.Diagnostics.Logger
{
public sealed class MqttNetLogMessage
{

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