Compare commits

...
105 Commits
Author SHA1 Message Date
Christian Kratky a7204234f6 Update docs. 2019-03-24 10:40:59 +01:00
Christian Kratky e8c33077b7 Update build script. 2019-03-24 10:35:42 +01:00
Christian Kratky bd86dae8a9 Update build script. 2019-03-24 10:19:43 +01:00
Christian Kratky 7188aaf79d Merge branch 'master' into develop 2019-03-24 10:06:09 +01:00
Christian Kratky bafcc81672 Update nuspecs. 2019-03-24 10:05:45 +01:00
Christian Kratky d0f1659eec Remove obsolete methods. 2019-03-24 10:05:38 +01:00
Christian Kratky 11a5b5a080 Add queue with async support to free up threads. 2019-03-22 23:12:37 +01:00
Christian Kratky 7ed5580352 Fix breaking changes. 2019-03-22 21:08:38 +01:00
Christian Kratky efc6f124b4 Merge remote-tracking branch 'origin/develop' into develop 2019-03-22 20:37:16 +01:00
Christian Kratky 7c3d88f62d Add validation for topics before publishing. 2019-03-22 20:37:14 +01:00
Christian b383a54416 Merge pull request #547 from vbBerni/develop
Fix Managed Client should dispose inner mqtt client #527
2019-03-22 20:31:24 +01:00
Christian Kratky 673fc5e20c Add windows startup script. 2019-03-16 09:55:45 +01:00
Christian Kratky f2b9642712 Add linuc startup script. 2019-03-16 09:55:15 +01:00
Christian 4f03f22afb Update README.md 2019-03-13 21:22:24 +01:00
Christian 27a4af6070 Update README.md 2019-03-13 21:20:09 +01:00
Christian 0184ce400c Update README.md 2019-03-13 21:19:55 +01:00
Christian Kratky 653ea8a102 Add MQTTnet debug logging. 2019-03-11 21:32:36 +01:00
Christian Kratky 3450889e84 Refactor settings. 2019-03-11 21:15:02 +01:00
Christian Kratky 1311535eed Added support for persisted retained messages. 2019-03-11 20:15:12 +01:00
Christian Kratky e1799ea6a3 Extend configuration. 2019-03-11 18:43:39 +01:00
Christian Kratky cd76f434e3 Fix breaking changes. 2019-03-10 20:12:04 +01:00
Christian Kratky fd91170aee Merge branch 'master' into develop 2019-03-10 20:00:49 +01:00
Christian Kratky cd1bfc3a10 Auto stash before merge of "develop" and "feature/server-project-bootstrap" 2019-03-10 19:58:02 +01:00
Christian Kratky f074e9ed04 Merge branch 'feature/server-project-bootstrap' into develop 2019-03-10 19:57:32 +01:00
Christian Kratky 562a5304f6 Add flag for secure connections. 2019-03-10 19:52:55 +01:00
Kiarash 1e697a94a1 Add Kesterl Settings into appsettings 2019-03-10 17:18:20 +01:00
Kiarash 0e2c3abd05 Add missing method for setting encrpted IPv6 address.
* Clean up
2019-03-10 16:34:18 +01:00
Kiarash 7321774c45 Merge branch 'feature/server-project-bootstrap' of https://github.com/chkr1011/MQTTnet into feature/server-project-bootstrap 2019-03-10 16:02:45 +01:00
Kiarash 4a65cab769 Merge branch 'feature/server-project-bootstrap' of https://github.com/chkr1011/MQTTnet into feature/server-project-bootstrap 2019-03-10 15:59:35 +01:00
Christian Kratky 510c3bd0bf Move logger into WebSocketServerAdapter instead of creating a new instance. 2019-03-10 15:58:55 +01:00
Kiarash 0c056b511e Fix and clean up 2019-03-10 15:56:11 +01:00
Christian Kratky 2ca6866c5e Remove obsolete code. 2019-03-10 14:55:17 +01:00
Kiarash ebcce9b8be Merge branch 'feature/server-project-bootstrap' of https://github.com/chkr1011/MQTTnet into feature/server-project-bootstrap 2019-03-10 13:58:37 +01:00
Kiarash 9e7c70e649 Add new configuartion model
Added new models for review
2019-03-10 13:58:18 +01:00
Christian Kratky bef0860e53 Add support for WebSocket connections. 2019-03-10 12:10:11 +01:00
Christian Kratky 67c9177731 Merge branch 'master' of https://github.com/chkr1011/MQTTnet 2019-03-10 10:12:26 +01:00
Christian Kratky 007991f073 Update docs. 2019-03-10 10:11:43 +01:00
Christian 532dddeb6c Merge pull request #526 from JohBa/patch-2
Don't cancel token before stop session manager
2019-03-10 10:08:46 +01:00
Christian Kratky 9070087fad Add flag for indicating if application message processing has failed. 2019-03-10 09:51:49 +01:00
Christian Kratky de8bcb7f37 Update docs. 2019-03-09 19:24:04 +01:00
Christian 5090cfb411 Fix memory leak in AsyncAutoResetEvent. 2019-03-09 19:20:34 +01:00
Christian Kratky 6e64d653c5 Hide connect password from logs. 2019-03-08 23:48:49 +01:00
Christian Kratky 461bc6e594 Add MQTTnet.Server to NoUWP solution. 2019-03-08 23:48:28 +01:00
Christian Kratky 0d95dc563c Update documents. 2019-03-08 23:48:07 +01:00
Christian Kratky 03bfc5f00b Refactor event handler namings. 2019-03-08 23:47:51 +01:00
Christian Kratky 8708609498 Add more function mappings for MQTTnet Server. 2019-03-06 22:42:00 +01:00
Christian dffc27c1f4 Migrate events in server to async handlers. 2019-03-05 21:37:30 +01:00
Christian 6f5f507323 Extend MQTTnet.Server application. 2019-03-05 21:37:00 +01:00
Christian 26d6d817cd Fix memory leak in AsyncAutoResetEvent. 2019-03-05 07:47:31 +01:00
Kiarash Ghiaseddin b68247f56d WIP
First draft for MQTTnetServer project
* Basic support for settings
* Nasty code for setting up kestrel from appsettings.json
2019-03-04 21:13:22 +01:00
Christian 3b02a5f6aa Fix NullRefException when cancelling the server. 2019-02-23 10:40:17 +01:00
Christian 09718a00e7 Add handlers to variable before using to avoid NullRefExceptions. 2019-02-23 10:13:12 +01:00
Christian ecc2cd5f0c Merge pull request #555 from BennyM/develop
When no handlers are configured the client should not blow up
2019-02-23 10:07:24 +01:00
Christian 618580dc15 Merge pull request #553 from dpsenner/feature/MqttClient-add-missing-async-extensions
Add extension methods that provide a simpler api for IMqttClient and IManagedMqttClient
2019-02-22 20:28:53 +01:00
BennyM 4860cec6c7 When no handlers are configured the client should not blow up 2019-02-19 21:40:11 +01:00
Dominik Psenner b9e0dbf150 Add extension methods that provide a simpler api for IMqttClient and IManagedMqttClient connect/disconnect when the handlers do not need async/await 2019-02-18 08:02:09 +01:00
vbBerni 2a4eb460da StopAsync now waits until the managed client is stoped and more typical disposing of internal client 2019-02-16 22:23:45 +01:00
Christian 087c622122 Merge pull request #551 from dpsenner/feature/add-async-handlers-for-connected-and-disconnected
Add async handler interfaces for connected and disconnected to allow async code on connect
2019-02-16 19:04:06 +01:00
Dominik Psenner 2000de15e7 Add async handlers for connect and disconnect, too
.. this allows a client to properly async publish on connect.
2019-02-15 17:02:33 +01:00
Dominik Psenner 5e6b902430 Add convenience extensions to the managed client 2019-02-15 16:55:52 +01:00
Dominik Psenner a1f414a06d Add async handler interfaces for connected and disconnected to allow async code to execute on connect 2019-02-15 15:02:15 +01:00
Berni de3a39bdc2 Fix Managed Client should dispose inner mqtt client #527 2019-02-07 16:46:58 +01:00
Christian 6c5c9e5bf2 Merge pull request #540 from vbBerni/develop
fix logger logId not used in ManagedMqttClient
2019-01-31 19:57:09 +01:00
Berni 506873ed5b fix logger logId not used in ManagedMqttClient 2019-01-31 15:50:01 +01:00
Christian Kratky 6a2bded181 Refactor session and connection handling in server. Fix QoS level 2 issues. 2019-01-27 11:15:32 +01:00
Christian Kratky ebf0e91ab8 Refactor subscribe and unsubscribe to support MQTTv5. 2019-01-21 21:38:39 +01:00
Johannes Bäurle 107e96ff7d Don't cancel token before stop session manager 2019-01-07 11:54:10 +01:00
Christian Kratky 0d6e244f84 Refactor UnitTests. 2019-01-06 13:31:27 +01:00
Christian Kratky 96a67579e2 Improve session handling. 2019-01-01 19:18:54 +01:00
Christian Kratky 4cba814716 Cleanup. 2018-12-27 23:10:52 +01:00
Christian Kratky b527059b72 Fix session takeover. 2018-12-27 23:05:57 +01:00
Christian Kratky c0507fcc55 Refactor interceptors etc. to support async. 2018-12-27 22:41:41 +01:00
Christian 38f2453c92 Update README.md 2018-12-22 20:25:53 +01:00
Christian Kratky 058bb65d31 Merge from develop. 2018-12-22 09:40:47 +01:00
Christian Kratky 38b215e9b5 Merge branch 'develop' into feature/MQTTv5 2018-12-22 09:38:12 +01:00
Christian Kratky 45df33c9a5 Update nuspec. 2018-12-22 09:33:44 +01:00
Christian Kratky 89becaa191 Fix build script. 2018-12-22 09:28:51 +01:00
Christian Kratky 6c87e4e039 Replace nuget version with placeholder. 2018-12-22 09:26:52 +01:00
Christian Kratky cbe06d6b87 Merge from master. 2018-12-21 00:21:42 +01:00
Christian 4fed80c2d0 Update appveyor.yml 2018-12-20 21:08:07 +01:00
Christian Kratky 74e4fd8b3f Update test certificate. 2018-12-20 20:54:37 +01:00
Christian Kratky cc113a6c34 Refactor namespaces. 2018-12-20 20:53:29 +01:00
Christian fca9876111 Delete .travis.yml 2018-12-19 00:10:10 +01:00
Christian ac9f6a36f8 Update appveyor.yml 2018-12-19 00:06:51 +01:00
Christian 929b60e044 Update appveyor.yml 2018-12-18 21:32:46 +01:00
Christian acb433dd4c Update appveyor.yml 2018-12-18 20:23:18 +01:00
Christian c4e8bf7f39 Update appveyor.yml 2018-12-18 20:12:09 +01:00
Christian ba4fdfb51c Merge pull request #497 from cquesnel/feature/add-websocket-parameters
Add custom WebSocket parameters
2018-12-14 21:37:04 +01:00
Charles Quesnel dfda916283 Use default parameter instead of overload 2018-12-13 14:54:31 -05:00
Charles Quesnel 40326df9a9 Fix typo in 'Cookie' 2018-12-13 14:08:53 -05:00
Charles Quesnel a94444098a Remove unused usings 2018-12-13 13:01:34 -05:00
Charles Quesnel 0c13e7a504 Add overload to allow passing custom request headers and cookies when connecting with WS 2018-12-13 12:56:47 -05:00
Christian Kratky 0dba57fe75 Cleanup code. 2018-12-09 21:04:14 +01:00
Christian Kratky 6483f896c1 Merge from develop. 2018-12-09 20:48:46 +01:00
Christian Kratky 8fa9943c3d Merge branch 'master' into develop 2018-12-09 19:44:17 +01:00
Christian Kratky 0f5fb8aa3a Add more pacakges. 2018-11-28 21:11:23 +01:00
Christian Kratky 7baff7c080 Refactor property definition for packets. 2018-11-23 22:50:44 +01:00
Christian Kratky 7292cedd2f Encode ConnAck packet. 2018-11-19 00:17:19 +01:00
Christian Kratky 48d5afa799 Refactor namings of serializers etc. 2018-11-19 00:14:15 +01:00
Christian Kratky 3db22eea51 Remove dedicated V3 messages. 2018-11-18 22:04:39 +01:00
Christian Kratky 2fbb44ec66 Add more property implementations. 2018-11-18 21:54:40 +01:00
Christian Kratky ace25af0e8 Added package properties. 2018-11-16 23:11:26 +01:00
Christian Kratky 8d78455414 Fix breaking changes. 2018-11-16 22:18:59 +01:00
Christian Kratky fed16d7710 Merge from develop. 2018-11-16 22:16:21 +01:00
Christian Kratky 5a59cd278c Add serializer adapter. 2018-08-06 20:36:15 +02:00
329 changed files with 12222 additions and 3631 deletions
+1
View File
@@ -235,6 +235,7 @@ FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
/*/**/package-lock.json
# Typescript v1 declaration files
typings/
-10
View File
@@ -1,10 +0,0 @@
language: csharp
solution: MQTTnet.sln
matrix:
include:
- dotnet: 2.0.0
mono: none
dist: trusty
env: DOTNETCORE=1 # optional, can be used to take different code paths in your script
- mono: latest
+3 -2
View File
@@ -11,10 +11,11 @@
<requireLicenseAcceptance>false</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-2018</copyright>
<copyright>Copyright Christian Kratky 2016-2019</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.5" />
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.3" />
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.1.1" />
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.1.1" />
@@ -11,10 +11,10 @@
<requireLicenseAcceptance>false</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-2018</copyright>
<copyright>Copyright Christian Kratky 2016-2019</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.5" />
<dependency id="MQTTnet" version="$nugetVersion" />
</dependencies>
</metadata>
@@ -37,4 +37,4 @@
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net471\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net472\"/>
</files>
</package>
</package>
+3 -3
View File
@@ -11,10 +11,10 @@
<requireLicenseAcceptance>false</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-2018</copyright>
<copyright>Copyright Christian Kratky 2016-2019</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.5" />
<dependency id="MQTTnet" version="$nugetVersion" />
</dependencies>
</metadata>
@@ -37,4 +37,4 @@
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net471\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net472\"/>
</files>
</package>
</package>
+36
View File
@@ -0,0 +1,36 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>MQTTnet.NETStandard</id>
<version>0.0.0</version>
<authors>Christian Kratky</authors>
<owners>Christian Kratky</owners>
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). This package contains the .NET Standard version of the library only.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2019</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<group targetFramework="netstandard1.3">
<dependency id="NETStandard.Library" version="1.3.0" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="NETStandard.Library" version="2.0.0" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
</dependencies>
</metadata>
<files>
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
</files>
</package>
+27 -14
View File
@@ -10,21 +10,34 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
<releaseNotes> * [Core] Updated nuget packages due to security issues.
* [Client] Fixed wrong behavior of publish method when client is disconnecting (thanks to @PaulFake).
* [Client] Added readonly property for accessing options.
* [ManagedClient] Added max pending messages count option.
* [ManagedClient] Add pending messages overflow strategy option.
* [ManagedClient] Fixed an issue which deletes the wrong message from the internal queue (thanks to @PaulFake).
* [ManagedClient] Added readonly property for accessing options.
* [Server] Added new method which exposes all retained messages.
* [Server] Removed (wrong) setter from the server options interface.
* [Server] Fixed cpu spike in case a client disconnects (issue 421).
* [Server] Fixed concurrent sends with Aspnetcore.Connections.Abstractions based transport.
* [Server] Fixed wrong retain flag when distributing application messages (thanks to @trev0115).
* [Server] Fixed issue which closes a connection when reconnecting with the same client ID (thanks to @fogzot).
<releaseNotes> * [Core] Added support for MQTTv5 packages.
* [Core] Performance improvements (removed several exceptions).
* [Core] Removed obsolete methods.
* [Core] Fixed a memory leak when processing lots of messages (thanks to @tschanko)
* [Core] Added more overloads for MQTT factory.
* [Core] The client password is now hidden from the logs (replaced with **** if set).
* [Client] Added validation of topics before publishing.
* [Client] Added new MQTTv5 features to options builder.
* [Client] Added uniform API across all supported MQTT versions (BREAKING CHANGE!)
* [Client] The client will now avoid sending an ACK if an exception has been thrown in message handler (thanks to @ramonsmits).
* [Client] Fixed issues in QoS 2 handling which leads to message loss.
* [Client] Replaced all events with proper async compatible handlers (BREAKING CHANGE!).
* [ManagedClient] Replaced all events with proper async compatible handlers (BREAKING CHANGE!).
* [ManagedClient] The log ID is now propagated to the internal client (thanks to @vbBerni).
* [ManagedClient] Added validation of topics before publishing.
* [ManagedClient] The internal MQTT client is now closed properly (thanks to @vbBerni).
* [Server] Added support for MQTTv5 clients. The server will still return _success_ for all cases at the moment even if more granular codes are available.
* [Server] Fixed issues in QoS 2 handling which leads to message loss.
* [Server] Replaced all events with proper async compatible handlers (BREAKING CHANGE!).
* [Server] The used logger instance is now propagated to the WebSocket server adapter.
* [Server] Added the flag "IsSecureConnection" which is set to true when the connection is encrypted.
* [Server] Fixed wrong will message behavior when stopping server (thanks to @JohBa)
* [Server] Added validation of topics before publishing.
* [MQTTnet Server] Added as first Alpha version of standalone cross platform MQTT server.
* [Note] Due to MQTTv5 a lot of new classes were introduced. This required adding new namespaces as well. Most classes are backward compatible but new namespaces must be added.
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<copyright>Copyright Christian Kratky 2016-2019</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<group targetFramework="netstandard1.3">
+14 -2
View File
@@ -39,9 +39,21 @@ if ($path) {
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path .\NuGet
Copy-Item MQTTnet.AspNetCore.nuspec -Destination MQTTnet.AspNetCore.nuspec.old -Force
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
Copy-Item MQTTnet.Extensions.Rpc.nuspec -Destination MQTTnet.Extensions.Rpc.nuspec.old -Force
(Get-Content MQTTnet.Extensions.Rpc.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.Rpc.nuspec
Copy-Item MQTTnet.Extensions.ManagedClient.nuspec -Destination MQTTnet.Extensions.ManagedClient.nuspec.old -Force
(Get-Content MQTTnet.Extensions.ManagedClient.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.ManagedClient.nuspec
New-Item -ItemType Directory -Force -Path .\NuGet
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.NETStandard.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
Move-Item MQTTnet.AspNetCore.nuspec.old -Destination MQTTnet.AspNetCore.nuspec -Force
Move-Item MQTTnet.Extensions.Rpc.nuspec.old -Destination MQTTnet.Extensions.Rpc.nuspec -Force
Move-Item MQTTnet.Extensions.ManagedClient.nuspec.old -Destination MQTTnet.Extensions.ManagedClient.nuspec -Force
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 Christian Kratky
MQTTnet Copyright (c) 2016-2019 Christian Kratky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+38 -19
View File
@@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2010
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
@@ -45,6 +43,10 @@ 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.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -57,22 +59,6 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.ActiveCfg = Debug|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.Build.0 = Debug|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.ActiveCfg = Debug|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.Build.0 = Debug|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.ActiveCfg = Debug|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.Build.0 = Debug|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.Build.0 = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.ActiveCfg = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.Build.0 = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.ActiveCfg = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
@@ -201,12 +187,43 @@ 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
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|ARM.ActiveCfg = Debug|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|ARM.Build.0 = Debug|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x64.ActiveCfg = Debug|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x64.Build.0 = Debug|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x86.ActiveCfg = Debug|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x86.Build.0 = Debug|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|Any CPU.Build.0 = Release|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|ARM.ActiveCfg = Release|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|ARM.Build.0 = Release|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x64.ActiveCfg = Release|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x64.Build.0 = Release|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x86.ActiveCfg = Release|Any CPU
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x86.Build.0 = Release|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|ARM.ActiveCfg = Debug|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|ARM.Build.0 = Debug|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x64.ActiveCfg = Debug|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x64.Build.0 = Debug|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x86.ActiveCfg = Debug|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x86.Build.0 = Debug|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|Any CPU.Build.0 = Release|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|ARM.ActiveCfg = Release|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|ARM.Build.0 = Release|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x64.ActiveCfg = Release|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x64.Build.0 = Release|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x86.ActiveCfg = Release|Any CPU
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
@@ -215,6 +232,8 @@ 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}
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7} = {32A630A7-2598-41D7-B625-204CD906F5FB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
+20 -1
View File
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2010
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
EndProject
@@ -47,6 +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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -226,6 +228,22 @@ Global
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|ARM.ActiveCfg = Debug|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|ARM.Build.0 = Debug|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x64.ActiveCfg = Debug|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x64.Build.0 = Debug|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x86.ActiveCfg = Debug|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x86.Build.0 = Debug|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|Any CPU.Build.0 = Release|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|ARM.ActiveCfg = Release|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|ARM.Build.0 = Release|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x64.ActiveCfg = Release|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x64.Build.0 = Release|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x86.ActiveCfg = Release|Any CPU
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -241,6 +259,7 @@ Global
{998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B} = {32A630A7-2598-41D7-B625-204CD906F5FB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
+1
View File
@@ -7,6 +7,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
+21 -6
View File
@@ -20,12 +20,14 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* Async support
* TLS 1.2 support for client and server (but not UWP servers)
* Extensible communication channels (i.e. In-Memory, TCP, TCP+TLS, WS)
* Extensible communication channels (e.g. In-Memory, TCP, TCP+TLS, WS)
* Lightweight (only the low level implementation of MQTT, no overhead)
* Performance optimized (processing ~70.000 messages / second)*
* Uniform API across all supported versions of the MQTT protocol
* Interfaces included for mocking and testing
* Access to internal trace messages
* Unit tested (~100 tests)
* Unit tested (~150 tests)
* No external dependencies
\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
@@ -48,6 +50,17 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* WebSockets supported (via ASP.NET Core 2.0, separate nuget)
* A custom message interceptor can be added which allows transforming or extending every received application message
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
* Connect clients with different protocol versions at the same time.
## MQTTnet Server
_MQTTnet Server_ is a standalone cross platform MQTT server (like mosquitto) basing on this library. It has the following features.
* Running portable (no installation required)
* Runs und Windows, Linux, macOS, Raspberry Pi
* Python scripting support for manipulating messages, validation of clients, building business logic etc.
* Supports WebSocket and TCP (with and without TLS) connections
* Provides a HTTP based API (including Swagger endpoint)
* Extensive configuration parameters and customization supported
## Supported frameworks
@@ -62,7 +75,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
## Supported MQTT versions
* 5.0.0 (planned)
* 5.0.0
* 3.1.1
* 3.1.0
@@ -79,7 +92,7 @@ Please find examples and the documentation at the Wiki of this repository (<http
If you want to contribute to this project just create a pull request. But only pull requests which are matching the code style of this library will be accepted. Before creating a pull request please have a look at the library to get an overview of the required style.
Also additions and updates in the Wiki are welcome.
This project also listed at Open Collective (https://opencollective.com/mqttnet).
**This project, like all projects requires time and money. If you feel compelled to support the development team, you can do so through Open Collective (https://opencollective.com/mqttnet)**.
## References
@@ -93,9 +106,11 @@ This library is used in the following projects:
If you use this library and want to see your project here please create a pull request.
## MIT License
## License
Copyright (c) 2017-2018 Christian Kratky
MIT License
MQTTnet Copyright (c) 2016-2019 Christian Kratky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -30,8 +30,7 @@ namespace MQTTnet.AspNetCore
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol))
{
var endpoint = $"{context.Connection.RemoteIpAddress}:{context.Connection.RemotePort}";
await adapter.RunWebSocketConnectionAsync(webSocket, endpoint);
await adapter.RunWebSocketConnectionAsync(webSocket, context);
}
});
@@ -3,8 +3,9 @@ using System.Net;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore.Client
{
@@ -14,15 +15,13 @@ namespace MQTTnet.AspNetCore.Client
{
if (options == null) throw new ArgumentNullException(nameof(options));
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
switch (options.ChannelOptions)
{
case MqttClientTcpOptions tcpOptions:
{
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
var tcpConnection = new TcpConnection(endpoint);
return new MqttConnectionContext(serializer, tcpConnection);
return new MqttConnectionContext(new MqttPacketFormatterAdapter(options.ProtocolVersion), tcpConnection);
}
default:
{
@@ -3,27 +3,26 @@ using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Serializer;
using System;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore
{
public class MqttConnectionContext : IMqttChannelAdapter
{
public MqttConnectionContext(
IMqttPacketSerializer packetSerializer,
ConnectionContext connection)
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
{
PacketSerializer = packetSerializer;
Connection = connection;
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
}
public string Endpoint => Connection.ConnectionId;
public bool IsSecureConnection => false; // TODO: Fix detection.
public ConnectionContext Connection { get; }
public IMqttPacketSerializer PacketSerializer { get; }
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public event EventHandler ReadingPacketStarted;
public event EventHandler ReadingPacketCompleted;
@@ -74,7 +73,7 @@ namespace MQTTnet.AspNetCore
{
if (!buffer.IsEmpty)
{
if (PacketSerializer.TryDeserialize(buffer, out var packet, out consumed, out observed))
if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed))
{
return packet;
}
@@ -107,9 +106,9 @@ namespace MQTTnet.AspNetCore
return null;
}
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
{
var buffer = PacketSerializer.Serialize(packet).AsMemory();
var buffer = PacketFormatterAdapter.Encode(packet).AsMemory();
var output = Connection.Transport.Output;
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -1,16 +1,16 @@
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using MQTTnet.Adapter;
using MQTTnet.Serializer;
using MQTTnet.Server;
using System;
using System.Threading.Tasks;
using MQTTnet.Formatter;
namespace MQTTnet.AspNetCore
{
public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
{
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
{
public Action<MqttServerAdapterClientAcceptedEventArgs> ClientAcceptedHandler { get; set; }
public override async Task OnConnectedAsync(ConnectionContext connection)
{
@@ -21,14 +21,12 @@ namespace MQTTnet.AspNetCore
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
}
var serializer = new MqttPacketSerializer();
using (var adapter = new MqttConnectionContext(serializer, connection))
using (var adapter = new MqttConnectionContext(new MqttPacketFormatterAdapter(), connection))
{
var args = new MqttServerAdapterClientAcceptedEventArgs(adapter);
ClientAccepted?.Invoke(this, args);
ClientAcceptedHandler?.Invoke(args);
await args.SessionTask;
await args.SessionTask.ConfigureAwait(false);
}
}
@@ -41,7 +39,7 @@ namespace MQTTnet.AspNetCore
{
return Task.CompletedTask;
}
public void Dispose()
{
}
@@ -1,17 +1,27 @@
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Serializer;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore
{
public class MqttWebSocketServerAdapter : IMqttServerAdapter
{
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
private readonly IMqttNetChildLogger _logger;
public MqttWebSocketServerAdapter(IMqttNetChildLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateChildLogger(nameof(MqttTcpServerAdapter));
}
public Action<MqttServerAdapterClientAcceptedEventArgs> ClientAcceptedHandler { get; set; }
public Task StartAsync(IMqttServerOptions options)
{
@@ -23,24 +33,30 @@ namespace MQTTnet.AspNetCore
return Task.CompletedTask;
}
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, string endpoint)
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
{
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
var clientAdapter = new MqttChannelAdapter(new MqttWebSocketChannel(webSocket, endpoint), new MqttPacketSerializer(), new MqttNetLogger().CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
var isSecureConnection = clientCertificate != null;
clientCertificate?.Dispose();
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection);
var clientAdapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(), _logger.CreateChildLogger(nameof(MqttWebSocketServerAdapter)));
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
ClientAccepted?.Invoke(this, eventArgs);
ClientAcceptedHandler?.Invoke(eventArgs);
if (eventArgs.SessionTask != null)
{
await eventArgs.SessionTask.ConfigureAwait(false);
}
}
public void Dispose()
{
StopAsync().GetAwaiter().GetResult();
}
}
}
@@ -2,19 +2,22 @@
using System.Buffers;
using MQTTnet.Adapter;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.AspNetCore
{
public static class ReaderExtensions
{
public static bool TryDeserialize(this IMqttPacketSerializer serializer, in ReadOnlySequence<byte> input, out MqttBasePacket packet, out SequencePosition consumed, out SequencePosition observed)
public static bool TryDecode(this MqttPacketFormatterAdapter formatter, in ReadOnlySequence<byte> input, out MqttBasePacket packet, out SequencePosition consumed, out SequencePosition observed)
{
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
packet = null;
consumed = input.Start;
observed = input.End;
var copy = input;
if (copy.Length < 2)
{
return false;
@@ -33,7 +36,7 @@ namespace MQTTnet.AspNetCore
var bodySlice = copy.Slice(0, bodyLength);
var buffer = bodySlice.GetArray();
packet = serializer.Deserialize(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length)));
packet = formatter.Decode(new ReceivedMqttPacket(fixedheader, new MqttPacketBodyReader(buffer, 0, buffer.Length), buffer.Length + 2));
consumed = bodySlice.End;
observed = bodySlice.End;
return true;
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
public class ApplicationMessageProcessedHandlerDelegate : IApplicationMessageProcessedHandler
{
private readonly Func<ApplicationMessageProcessedEventArgs, Task> _handler;
public ApplicationMessageProcessedHandlerDelegate(Action<ApplicationMessageProcessedEventArgs> handler)
{
if (handler == null) throw new ArgumentNullException(nameof(handler));
_handler = context =>
{
handler(context);
return Task.FromResult(0);
};
}
public ApplicationMessageProcessedHandlerDelegate(Func<ApplicationMessageProcessedEventArgs, Task> handler)
{
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
}
public Task HandleApplicationMessageProcessedAsync(ApplicationMessageProcessedEventArgs eventArgs)
{
return _handler(eventArgs);
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
public class ApplicationMessageSkippedHandlerDelegate : IApplicationMessageSkippedHandler
{
private readonly Func<ApplicationMessageSkippedEventArgs, Task> _handler;
public ApplicationMessageSkippedHandlerDelegate(Action<ApplicationMessageSkippedEventArgs> handler)
{
if (handler == null) throw new ArgumentNullException(nameof(handler));
_handler = eventArgs =>
{
handler(eventArgs);
return Task.FromResult(0);
};
}
public ApplicationMessageSkippedHandlerDelegate(Func<ApplicationMessageSkippedEventArgs, Task> handler)
{
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
}
public Task HandleApplicationMessageSkippedAsync(ApplicationMessageSkippedEventArgs eventArgs)
{
return _handler(eventArgs);
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
public class ConnectingFailedHandlerDelegate : IConnectingFailedHandler
{
private readonly Func<ManagedProcessFailedEventArgs, Task> _handler;
public ConnectingFailedHandlerDelegate(Action<ManagedProcessFailedEventArgs> handler)
{
if (handler == null) throw new ArgumentNullException(nameof(handler));
_handler = eventArgs =>
{
handler(eventArgs);
return Task.FromResult(0);
};
}
public ConnectingFailedHandlerDelegate(Func<ManagedProcessFailedEventArgs, Task> handler)
{
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
}
public Task HandleConnectingFailedAsync(ManagedProcessFailedEventArgs eventArgs)
{
return _handler(eventArgs);
}
}
}
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
public interface IApplicationMessageProcessedHandler
{
Task HandleApplicationMessageProcessedAsync(ApplicationMessageProcessedEventArgs eventArgs);
}
}
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
public interface IApplicationMessageSkippedHandler
{
Task HandleApplicationMessageSkippedAsync(ApplicationMessageSkippedEventArgs eventArgs);
}
}
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
public interface IConnectingFailedHandler
{
Task HandleConnectingFailedAsync(ManagedProcessFailedEventArgs eventArgs);
}
}
@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -12,14 +13,14 @@ namespace MQTTnet.Extensions.ManagedClient
int PendingApplicationMessagesCount { get; }
IManagedMqttClientOptions Options { get; }
event EventHandler<MqttClientConnectedEventArgs> Connected;
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
IMqttClientConnectedHandler ConnectedHandler { get; set; }
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
event EventHandler<ApplicationMessageSkippedEventArgs> ApplicationMessageSkipped;
IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
IConnectingFailedHandler ConnectingFailedHandler { get; set; }
ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
Task StartAsync(IManagedMqttClientOptions options);
Task StopAsync();
@@ -1,5 +1,5 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
public interface ISynchronizingSubscriptionsFailedHandler
{
Task HandleSynchronizingSubscriptionsFailedAsync(ManagedProcessFailedEventArgs eventArgs);
}
}
@@ -4,13 +4,15 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Publishing;
using MQTTnet.Client.Receiving;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Protocol;
using MQTTnet.Server;
using MqttClientConnectedEventArgs = MQTTnet.Client.MqttClientConnectedEventArgs;
using MqttClientDisconnectedEventArgs = MQTTnet.Client.MqttClientDisconnectedEventArgs;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -25,21 +27,18 @@ namespace MQTTnet.Extensions.ManagedClient
private CancellationTokenSource _connectionCancellationToken;
private CancellationTokenSource _publishingCancellationToken;
private Task _maintainConnectionTask;
private ManagedMqttClientStorageManager _storageManager;
private bool _disposed = false;
private bool _subscriptionsNotPushed;
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetChildLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
_mqttClient.Connected += OnConnected;
_mqttClient.Disconnected += OnDisconnected;
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateChildLogger(nameof(ManagedMqttClient));
}
@@ -48,18 +47,36 @@ namespace MQTTnet.Extensions.ManagedClient
public int PendingApplicationMessagesCount => _messageQueue.Count;
public IManagedMqttClientOptions Options { get; private set; }
public event EventHandler<MqttClientConnectedEventArgs> Connected;
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
public IMqttClientConnectedHandler ConnectedHandler
{
get => _mqttClient.ConnectedHandler;
set => _mqttClient.ConnectedHandler = value;
}
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
public event EventHandler<ApplicationMessageSkippedEventArgs> ApplicationMessageSkipped;
public IMqttClientDisconnectedHandler DisconnectedHandler
{
get => _mqttClient.DisconnectedHandler;
set => _mqttClient.DisconnectedHandler = value;
}
public event EventHandler<MqttManagedProcessFailedEventArgs> ConnectingFailed;
public event EventHandler<MqttManagedProcessFailedEventArgs> SynchronizingSubscriptionsFailed;
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler
{
get => _mqttClient.ApplicationMessageReceivedHandler;
set => _mqttClient.ApplicationMessageReceivedHandler = value;
}
public IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
public IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
public IConnectingFailedHandler ConnectingFailedHandler { get; set; }
public ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
public async Task StartAsync(IManagedMqttClientOptions options)
{
ThrowIfDisposed();
if (options == null) throw new ArgumentNullException(nameof(options));
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
@@ -68,7 +85,7 @@ namespace MQTTnet.Extensions.ManagedClient
throw new NotSupportedException("The managed client does not support existing sessions.");
}
if (_connectionCancellationToken != null) throw new InvalidOperationException("The managed client is already started.");
if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
Options = options;
@@ -86,56 +103,86 @@ namespace MQTTnet.Extensions.ManagedClient
_connectionCancellationToken = new CancellationTokenSource();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token);
_maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
_logger.Info("Started");
}
public Task StopAsync()
public async Task StopAsync()
{
ThrowIfDisposed();
StopPublishing();
StopMaintainingConnection();
_messageQueue.Clear();
return Task.FromResult(0);
if (_maintainConnectionTask != null)
{
await Task.WhenAny(_maintainConnectionTask);
_maintainConnectionTask = null;
}
}
public Task PublishAsync(MqttApplicationMessage applicationMessage)
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
{
ThrowIfDisposed();
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
return PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build());
await PublishAsync(new ManagedMqttApplicationMessageBuilder().WithApplicationMessage(applicationMessage).Build()).ConfigureAwait(false);
return new MqttClientPublishResult();
}
public async Task PublishAsync(ManagedMqttApplicationMessage applicationMessage)
{
ThrowIfDisposed();
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage.Topic);
ManagedMqttApplicationMessage removedMessage = null;
lock (_messageQueue)
ApplicationMessageSkippedEventArgs applicationMessageSkippedEventArgs = null;
try
{
if (_messageQueue.Count >= Options.MaxPendingMessages)
lock (_messageQueue)
{
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
if (_messageQueue.Count >= Options.MaxPendingMessages)
{
_logger.Verbose("Skipping publish of new application message because internal queue is full.");
ApplicationMessageSkipped?.Invoke(this, new ApplicationMessageSkippedEventArgs(applicationMessage));
return;
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
{
_logger.Verbose("Skipping publish of new application message because internal queue is full.");
applicationMessageSkippedEventArgs = new ApplicationMessageSkippedEventArgs(applicationMessage);
return;
}
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
{
removedMessage = _messageQueue.RemoveFirst();
_logger.Verbose("Removed oldest application message from internal queue because it is full.");
applicationMessageSkippedEventArgs = new ApplicationMessageSkippedEventArgs(removedMessage);
}
}
if (Options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
_messageQueue.Enqueue(applicationMessage);
}
}
finally
{
if (applicationMessageSkippedEventArgs != null)
{
var applicationMessageSkippedHandler = ApplicationMessageSkippedHandler;
if (applicationMessageSkippedHandler != null)
{
removedMessage = _messageQueue.RemoveFirst();
_logger.Verbose("Removed oldest application message from internal queue because it is full.");
ApplicationMessageSkipped?.Invoke(this, new ApplicationMessageSkippedEventArgs(removedMessage));
await applicationMessageSkippedHandler.HandleApplicationMessageSkippedAsync(applicationMessageSkippedEventArgs).ConfigureAwait(false);
}
}
_messageQueue.Enqueue(applicationMessage);
}
if (_storageManager != null)
{
if (removedMessage != null)
@@ -149,6 +196,8 @@ namespace MQTTnet.Extensions.ManagedClient
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
{
ThrowIfDisposed();
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
lock (_subscriptions)
@@ -165,6 +214,8 @@ namespace MQTTnet.Extensions.ManagedClient
public Task UnsubscribeAsync(IEnumerable<string> topics)
{
ThrowIfDisposed();
if (topics == null) throw new ArgumentNullException(nameof(topics));
lock (_subscriptions)
@@ -184,8 +235,31 @@ namespace MQTTnet.Extensions.ManagedClient
public void Dispose()
{
_connectionCancellationToken?.Dispose();
_publishingCancellationToken?.Dispose();
if (_disposed)
{
return;
}
_disposed = true;
StopPublishing();
StopMaintainingConnection();
if (_maintainConnectionTask != null)
{
Task.WaitAny(_maintainConnectionTask);
_maintainConnectionTask = null;
}
_mqttClient.Dispose();
}
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(ManagedMqttClient));
}
}
private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
@@ -206,8 +280,19 @@ namespace MQTTnet.Extensions.ManagedClient
}
finally
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
_logger.Info("Stopped");
if (!_disposed)
{
try
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error(exception, "Error while disconnecting.");
}
_logger.Info("Stopped");
}
}
}
@@ -332,20 +417,20 @@ namespace MQTTnet.Extensions.ManagedClient
}
finally
{
ApplicationMessageProcessed?.Invoke(this, new ApplicationMessageProcessedEventArgs(message, transmitException));
ApplicationMessageProcessedHandler?.HandleApplicationMessageProcessedAsync(new ApplicationMessageProcessedEventArgs(message, transmitException)).GetAwaiter().GetResult();
}
}
private async Task SynchronizeSubscriptionsAsync()
{
_logger.Info(nameof(ManagedMqttClient), "Synchronizing subscriptions");
_logger.Info("Synchronizing subscriptions");
List<TopicFilter> subscriptions;
HashSet<string> unsubscriptions;
lock (_subscriptions)
{
subscriptions = _subscriptions.Select(i => new TopicFilter(i.Key, i.Value)).ToList();
subscriptions = _subscriptions.Select(i => new TopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList();
unsubscriptions = new HashSet<string>(_unsubscriptions);
_unsubscriptions.Clear();
@@ -362,12 +447,12 @@ namespace MQTTnet.Extensions.ManagedClient
{
if (unsubscriptions.Any())
{
await _mqttClient.UnsubscribeAsync(unsubscriptions).ConfigureAwait(false);
await _mqttClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false);
}
if (subscriptions.Any())
{
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
}
}
catch (Exception exception)
@@ -375,7 +460,11 @@ namespace MQTTnet.Extensions.ManagedClient
_logger.Warning(exception, "Synchronizing subscriptions failed.");
_subscriptionsNotPushed = true;
SynchronizingSubscriptionsFailed?.Invoke(this, new MqttManagedProcessFailedEventArgs(exception));
var synchronizingSubscriptionsFailedHandler = SynchronizingSubscriptionsFailedHandler;
if (SynchronizingSubscriptionsFailedHandler != null)
{
await synchronizingSubscriptionsFailedHandler.HandleSynchronizingSubscriptionsFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
}
}
}
@@ -393,26 +482,16 @@ namespace MQTTnet.Extensions.ManagedClient
}
catch (Exception exception)
{
ConnectingFailed?.Invoke(this, new MqttManagedProcessFailedEventArgs(exception));
var connectingFailedHandler = ConnectingFailedHandler;
if (connectingFailedHandler != null)
{
await connectingFailedHandler.HandleConnectingFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
}
return ReconnectionResult.NotConnected;
}
}
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
{
ApplicationMessageReceived?.Invoke(this, eventArgs);
}
private void OnDisconnected(object sender, MqttClientDisconnectedEventArgs eventArgs)
{
Disconnected?.Invoke(this, eventArgs);
}
private void OnConnected(object sender, MqttClientConnectedEventArgs eventArgs)
{
Connected?.Invoke(this, eventArgs);
}
private void StartPublishing()
{
if (_publishingCancellationToken != null)
@@ -440,4 +519,4 @@ namespace MQTTnet.Extensions.ManagedClient
_connectionCancellationToken = null;
}
}
}
}
@@ -1,11 +1,91 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Publishing;
using MQTTnet.Client.Receiving;
using MQTTnet.Protocol;
namespace MQTTnet.Extensions.ManagedClient
{
public static class ManagedMqttClientExtensions
{
public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, Func<MqttClientConnectedEventArgs, Task> handler)
{
if (handler == null)
{
client.ConnectedHandler = null;
return client;
}
client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(handler);
return client;
}
public static IManagedMqttClient UseConnectedHandler(this IManagedMqttClient client, Action<MqttClientConnectedEventArgs> handler)
{
if (handler == null)
{
client.ConnectedHandler = null;
return client;
}
client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(handler);
return client;
}
public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, Func<MqttClientDisconnectedEventArgs, Task> handler)
{
if (handler == null)
{
client.DisconnectedHandler = null;
return client;
}
client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(handler);
return client;
}
public static IManagedMqttClient UseDisconnectedHandler(this IManagedMqttClient client, Action<MqttClientDisconnectedEventArgs> handler)
{
if (handler == null)
{
client.DisconnectedHandler = null;
return client;
}
client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(handler);
return client;
}
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, Func<MqttApplicationMessageReceivedEventArgs, Task> handler)
{
if (handler == null)
{
client.ApplicationMessageReceivedHandler = null;
return client;
}
client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(handler);
return client;
}
public static IManagedMqttClient UseApplicationMessageReceivedHandler(this IManagedMqttClient client, Action<MqttApplicationMessageReceivedEventArgs> handler)
{
if (handler == null)
{
client.ApplicationMessageReceivedHandler = null;
return client;
}
client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(handler);
return client;
}
public static Task SubscribeAsync(this IManagedMqttClient managedClient, params TopicFilter[] topicFilters)
{
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
@@ -35,5 +115,89 @@ namespace MQTTnet.Extensions.ManagedClient
return managedClient.UnsubscribeAsync(topicFilters);
}
public static async Task PublishAsync(this IManagedMqttClient client, IEnumerable<MqttApplicationMessage> applicationMessages)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
foreach (var applicationMessage in applicationMessages)
{
await client.PublishAsync(applicationMessage).ConfigureAwait(false);
}
}
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, MqttApplicationMessage applicationMessage)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
return client.PublishAsync(applicationMessage, CancellationToken.None);
}
public static async Task PublishAsync(this IManagedMqttClient client, params MqttApplicationMessage[] applicationMessages)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
foreach (var applicationMessage in applicationMessages)
{
await client.PublishAsync(applicationMessage, CancellationToken.None).ConfigureAwait(false);
}
}
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return client.PublishAsync(builder => builder
.WithTopic(topic));
}
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return client.PublishAsync(builder => builder
.WithTopic(topic)
.WithPayload(payload));
}
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return client.PublishAsync(builder => builder
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel(qualityOfServiceLevel));
}
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return client.PublishAsync(builder => builder
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel(qualityOfServiceLevel)
.WithRetainFlag(retain));
}
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder, CancellationToken cancellationToken)
{
var message = builder(new MqttApplicationMessageBuilder()).Build();
return client.PublishAsync(message, cancellationToken);
}
public static Task<MqttClientPublishResult> PublishAsync(this IManagedMqttClient client, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
{
var message = builder(new MqttApplicationMessageBuilder()).Build();
return client.PublishAsync(message, CancellationToken.None);
}
}
}
@@ -1,5 +1,6 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
@@ -1,5 +1,6 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
namespace MQTTnet.Extensions.ManagedClient
@@ -30,7 +30,7 @@ namespace MQTTnet.Extensions.ManagedClient
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
{
_messages.Add(applicationMessage);
await SaveAsync().ConfigureAwait(false);
@@ -41,7 +41,7 @@ namespace MQTTnet.Extensions.ManagedClient
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
using (await _messagesLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
{
var index = _messages.IndexOf(applicationMessage);
if (index == -1)
@@ -0,0 +1,14 @@
using System;
namespace MQTTnet.Extensions.ManagedClient
{
public class ManagedProcessFailedEventArgs : EventArgs
{
public ManagedProcessFailedEventArgs(Exception exception)
{
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
}
public Exception Exception { get; }
}
}
@@ -1,24 +1,23 @@
using System;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
namespace MQTTnet.Extensions.ManagedClient
{
public static class MqttFactoryExtensions
{
public static IManagedMqttClient CreateManagedMqttClient(this IMqttClientFactory factory)
public static IManagedMqttClient CreateManagedMqttClient(this IMqttFactory factory)
{
if (factory == null) throw new ArgumentNullException(nameof(factory));
return new ManagedMqttClient(factory.CreateMqttClient(), new MqttNetLogger().CreateChildLogger());
return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger.CreateChildLogger());
}
public static IManagedMqttClient CreateManagedMqttClient(this IMqttClientFactory factory, IMqttNetLogger logger)
public static IManagedMqttClient CreateManagedMqttClient(this IMqttFactory factory, IMqttNetLogger logger)
{
if (factory == null) throw new ArgumentNullException(nameof(factory));
if (logger == null) throw new ArgumentNullException(nameof(logger));
return new ManagedMqttClient(factory.CreateMqttClient(), logger.CreateChildLogger());
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger.CreateChildLogger());
}
}
}
}
@@ -1,14 +0,0 @@
using System;
namespace MQTTnet.Extensions.ManagedClient
{
public class MqttManagedProcessFailedEventArgs : EventArgs
{
public MqttManagedProcessFailedEventArgs(Exception exception)
{
Exception = exception;
}
public Exception Exception { get; }
}
}
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
public class SynchronizingSubscriptionsFailedHandlerDelegate : ISynchronizingSubscriptionsFailedHandler
{
private readonly Func<ManagedProcessFailedEventArgs, Task> _handler;
public SynchronizingSubscriptionsFailedHandlerDelegate(Action<ManagedProcessFailedEventArgs> handler)
{
if (handler == null) throw new ArgumentNullException(nameof(handler));
_handler = context =>
{
handler(context);
return Task.FromResult(0);
};
}
public SynchronizingSubscriptionsFailedHandlerDelegate(Func<ManagedProcessFailedEventArgs, Task> handler)
{
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
}
public Task HandleSynchronizingSubscriptionsFailedAsync(ManagedProcessFailedEventArgs eventArgs)
{
return _handler(eventArgs);
}
}
}
+19 -5
View File
@@ -13,12 +13,17 @@ namespace MQTTnet.Extensions.Rpc
{
private readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
private readonly IMqttClient _mqttClient;
private readonly RpcAwareApplicationMessageReceivedHandler _applicationMessageReceivedHandler;
public MqttRpcClient(IMqttClient mqttClient)
{
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
_applicationMessageReceivedHandler = new RpcAwareApplicationMessageReceivedHandler(
mqttClient.ApplicationMessageReceivedHandler,
HandleApplicationMessageReceivedAsync);
_mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler;
}
public Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
@@ -45,6 +50,11 @@ namespace MQTTnet.Extensions.Rpc
throw new ArgumentException("The method name cannot contain /, + or #.");
}
if (!(_mqttClient.ApplicationMessageReceivedHandler is RpcAwareApplicationMessageReceivedHandler))
{
throw new InvalidOperationException("The application message received handler was modified.");
}
var requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{methodName}";
var responseTopic = requestTopic + "/response";
@@ -102,26 +112,30 @@ namespace MQTTnet.Extensions.Rpc
}
}
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
private Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var tcs))
{
return;
return Task.FromResult(0);
}
if (tcs.Task.IsCompleted || tcs.Task.IsCanceled)
{
return;
return Task.FromResult(0);
}
tcs.TrySetResult(eventArgs.ApplicationMessage.Payload);
return Task.FromResult(0);
}
public void Dispose()
{
_mqttClient.ApplicationMessageReceivedHandler = _applicationMessageReceivedHandler.OriginalHandler;
foreach (var tcs in _waitingCalls)
{
tcs.Value.SetCanceled();
tcs.Value.TrySetCanceled();
}
_waitingCalls.Clear();
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
using MQTTnet.Client.Receiving;
namespace MQTTnet.Extensions.Rpc
{
public class RpcAwareApplicationMessageReceivedHandler : IMqttApplicationMessageReceivedHandler
{
private readonly Func<MqttApplicationMessageReceivedEventArgs, Task> _handleReceivedApplicationMessageAsync;
public RpcAwareApplicationMessageReceivedHandler(
IMqttApplicationMessageReceivedHandler originalHandler,
Func<MqttApplicationMessageReceivedEventArgs, Task> handleReceivedApplicationMessageAsync)
{
OriginalHandler = originalHandler;
_handleReceivedApplicationMessageAsync = handleReceivedApplicationMessageAsync ?? throw new ArgumentNullException(nameof(handleReceivedApplicationMessageAsync));
}
public IMqttApplicationMessageReceivedHandler OriginalHandler { get; }
public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{
if (OriginalHandler != null)
{
await OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
}
await _handleReceivedApplicationMessageAsync(eventArgs).ConfigureAwait(false);
}
}
}
@@ -0,0 +1,11 @@
namespace MQTTnet.Server.Configuration
{
public class RetainedApplicationMessagesModel
{
public bool Persist { get; set; } = false;
public int WriteInterval { get; set; } = 10;
public string Filename { get; set; } = "RetainedApplicationMessages.json";
}
}
@@ -0,0 +1,50 @@
namespace MQTTnet.Server.Configuration
{
/// <summary>
/// Main Settings Model
/// </summary>
public class SettingsModel
{
/// <summary>
/// Set default connection timeout in seconds
/// </summary>
public int CommunicationTimeout { get; set; } = 15;
/// <summary>
/// Set 0 to disable connection backlogging
/// </summary>
public int ConnectionBacklog { get; set; }
/// <summary>
/// Enable support for persistent sessions
/// </summary>
public bool EnablePersistentSessions { get; set; } = false;
/// <summary>
/// Listen Settings
/// </summary>
public TcpEndPointModel TcpEndPoint { get; set; } = new TcpEndPointModel();
/// <summary>
/// Encryption Listen Settings
/// </summary>
public TcpEndPointModel EncryptedTcpEndPoint { get; set; } = new TcpEndPointModel();
/// <summary>
/// Settings for the Web Socket endpoint.
/// </summary>
public WebSocketEndPointModel WebSocketEndPoint { get; set; } = new WebSocketEndPointModel();
/// <summary>
/// Set limit for max pending messages per client
/// </summary>
public int MaxPendingMessagesPerClient { get; set; } = 250;
/// <summary>
/// The settings for retained messages.
/// </summary>
public RetainedApplicationMessagesModel RetainedApplicationMessages { get; set; } = new RetainedApplicationMessagesModel();
public bool EnableDebugLogging { get; set; } = false;
}
}
@@ -0,0 +1,125 @@
using System.IO;
using System.Net;
namespace MQTTnet.Server.Configuration
{
/// <summary>
/// Listen Entry Settings Model
/// </summary>
public class TcpEndPointModel
{
/// <summary>
/// Path to Certificate
/// </summary>
public string CertificatePath { get; set; }
/// <summary>
/// Enabled / Disable
/// </summary>
public bool Enabled { get; set; } = true;
/// <summary>
/// Listen Address
/// </summary>
public string IPv4 { get; set; }
/// <summary>
/// Listen Address
/// </summary>
public string IPv6 { get; set; }
/// <summary>
/// Listen Port
/// </summary>
public int Port { get; set; } = 1883;
/// <summary>
/// Read Certificate file
/// </summary>
/// <returns></returns>
public byte[] ReadCertificate()
{
if (string.IsNullOrEmpty(CertificatePath) || string.IsNullOrWhiteSpace(CertificatePath))
{
throw new FileNotFoundException("No path set");
}
if (!File.Exists(CertificatePath))
{
throw new FileNotFoundException($"Could not find Certificate in path: {CertificatePath}");
}
return File.ReadAllBytes(CertificatePath);
}
/// <summary>
/// Read IPv4
/// </summary>
/// <returns></returns>
public bool TryReadIPv4(out IPAddress address)
{
if (IPv4 == "*")
{
address = IPAddress.Parse("0.0.0.0");
return true;
}
if (IPv4 == "localhost")
{
address = IPAddress.Parse("127.0.0.1");
return true;
}
if (IPv4 == "disable")
{
address = IPAddress.None;
return true;
}
if (IPAddress.TryParse(IPv4, out var ip))
{
address = ip;
return true;
}
else
{
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.Parse("::");
return true;
}
if (IPv6 == "localhost")
{
address = IPAddress.Parse("::1");
return true;
}
if (IPv6 == "disable")
{
address = IPAddress.None;
return true;
}
if (IPAddress.TryParse(IPv6, out var ip))
{
address = ip;
return true;
}
else
{
throw new System.Exception($"Could not parse IPv6 address: {IPv6}");
}
}
}
}
@@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace MQTTnet.Server.Configuration
{
public class WebSocketEndPointModel
{
public bool Enabled { get; set; } = true;
public string Path { get; set; } = "/mqtt";
public int ReceiveBufferSize { get; set; } = 4096;
public int KeepAliveInterval { get; set; } = 120;
public List<string> AllowedOrigins { get; set; }
}
}
@@ -0,0 +1,42 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
namespace MQTTnet.Server.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody] string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Christian Kratky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,43 @@
using System;
using MQTTnet.Diagnostics;
namespace MQTTnet.Server.Logging
{
public class MqttNetChildLoggerWrapper : IMqttNetChildLogger
{
private readonly MqttNetLoggerWrapper _logger;
private readonly string _source;
public MqttNetChildLoggerWrapper(string source, MqttNetLoggerWrapper logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_source = source;
}
public IMqttNetChildLogger CreateChildLogger(string source = null)
{
return _logger.CreateChildLogger(source);
}
public void Verbose(string message, params object[] parameters)
{
_logger.Publish(MqttNetLogLevel.Verbose, _source, message, parameters, null);
}
public void Info(string message, params object[] parameters)
{
_logger.Publish(MqttNetLogLevel.Info, _source, message, parameters, null);
}
public void Warning(Exception exception, string message, params object[] parameters)
{
_logger.Publish(MqttNetLogLevel.Warning, _source, message, parameters, exception);
}
public void Error(Exception exception, string message, params object[] parameters)
{
_logger.Publish(MqttNetLogLevel.Error, _source, message, parameters, exception);
}
}
}
@@ -0,0 +1,42 @@
using System;
using Microsoft.Extensions.Logging;
using MQTTnet.Diagnostics;
namespace MQTTnet.Server.Logging
{
public class MqttNetLoggerWrapper : IMqttNetLogger
{
private readonly ILogger<MqttServer> _logger;
public MqttNetLoggerWrapper(ILogger<MqttServer> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
public IMqttNetChildLogger CreateChildLogger(string source = null)
{
return new MqttNetChildLoggerWrapper(source, this);
}
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
{
var convertedLogLevel = ConvertLogLevel(logLevel);
_logger.Log(convertedLogLevel, exception, message, parameters);
}
private static LogLevel ConvertLogLevel(MqttNetLogLevel logLevel)
{
switch (logLevel)
{
case MqttNetLogLevel.Error: return LogLevel.Error;
case MqttNetLogLevel.Warning: return LogLevel.Warning;
case MqttNetLogLevel.Info: return LogLevel.Information;
case MqttNetLogLevel.Verbose: return LogLevel.Debug;
}
return LogLevel.Debug;
}
}
}
@@ -0,0 +1,89 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<AssemblyName>MQTTnet.Server</AssemblyName>
<RootNamespace>MQTTnet.Server</RootNamespace>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<Company />
<Product>MQTTnet</Product>
<Description />
<Authors />
<PackageId />
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<LangVersion>latest</LangVersion>
<TypeScriptToolsVersion>3.3</TypeScriptToolsVersion>
</PropertyGroup>
<PropertyGroup>
<StartupObject></StartupObject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile></DocumentationFile>
<WarningsAsErrors>NU1605</WarningsAsErrors>
<NoWarn>1701;1702</NoWarn>
</PropertyGroup>
<ItemGroup>
<Content Remove="wwwroot\app\app.ts" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\app\app.js">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\app\app.js.map">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\Index.html">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="IronPython" Version="2.7.9" />
<PackageReference Include="IronPython.StdLib" Version="2.7.9" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="4.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="LICENSE">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="run.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="run.bat">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Scripts\readme.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Scripts\00_sample.py">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<TypeScriptCompile Include="wwwroot\app\app.ts">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</TypeScriptCompile>
</ItemGroup>
</Project>
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Server.Configuration;
using MQTTnet.Server.Logging;
namespace MQTTnet.Server.Mqtt
{
public class CustomMqttFactory
{
private readonly MqttFactory _mqttFactory;
public CustomMqttFactory(SettingsModel settings, ILogger<MqttServer> logger)
{
if (settings == null) throw new ArgumentNullException(nameof(settings));
if (logger == null) throw new ArgumentNullException(nameof(logger));
// It is important to avoid injecting the logger wrapper to ensure that no
// unused log messages are generated by the MQTTnet library. Debug logging
// has a huge performance impact.
if (settings.EnableDebugLogging)
{
var mqttNetLogger = new MqttNetLoggerWrapper(logger);
_mqttFactory = new MqttFactory(mqttNetLogger);
logger.LogWarning("Debug logging is enabled. Performance of MQTTnet Server is decreased!");
}
else
{
_mqttFactory = new MqttFactory();
}
Logger = _mqttFactory.DefaultLogger;
}
public IMqttNetLogger Logger { get; }
public IMqttServer CreateMqttServer(List<IMqttServerAdapter> adapters)
{
if (adapters == null) throw new ArgumentNullException(nameof(adapters));
return _mqttFactory.CreateMqttServer(adapters);
}
}
}
@@ -0,0 +1,66 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Protocol;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttApplicationMessageInterceptor : IMqttServerApplicationMessageInterceptor
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger _logger;
public MqttApplicationMessageInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttApplicationMessageInterceptor> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task InterceptApplicationMessagePublishAsync(MqttApplicationMessageInterceptorContext context)
{
try
{
var pythonContext = new PythonDictionary
{
{ "accept_publish", context.AcceptPublish },
{ "close_connection", context.CloseConnection },
{ "client_id", context.ClientId },
{ "topic", context.ApplicationMessage.Topic },
{ "qos", (int)context.ApplicationMessage.QualityOfServiceLevel },
{ "retain", context.ApplicationMessage.Retain }
};
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_application_message", pythonContext);
context.AcceptPublish = (bool)pythonContext.get("accept_publish", context.AcceptPublish);
context.CloseConnection = (bool)pythonContext.get("close_connection", context.CloseConnection);
context.ApplicationMessage.Topic = (string)pythonContext.get("topic", context.ApplicationMessage.Topic);
context.ApplicationMessage.QualityOfServiceLevel = (MqttQualityOfServiceLevel)(int)pythonContext.get("qos", (int)context.ApplicationMessage.QualityOfServiceLevel);
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while intercepting application message.");
}
return Task.CompletedTask;
}
// TODO: Create dump(object) method in wrapper (creates JSON and prints it).
public class PythonMqttApplicationMessageInterceptorContext
{
public bool accept_connection;
public bool accept_publish;
public string client_id;
public string topic;
public int qos;
public bool retain;
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttClientConnectedHandler : IMqttServerClientConnectedHandler
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger _logger;
public MqttClientConnectedHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientConnectedHandler> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task HandleClientConnectedAsync(MqttServerClientConnectedEventArgs eventArgs)
{
try
{
var pythonEventArgs = new PythonDictionary
{
{ "client_id", eventArgs.ClientId }
};
_pythonScriptHostService.InvokeOptionalFunction("on_client_connected", pythonEventArgs);
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while handling client connected event.");
}
return Task.CompletedTask;
}
}
}
@@ -0,0 +1,40 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttClientDisconnectedHandler : IMqttServerClientDisconnectedHandler
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger _logger;
public MqttClientDisconnectedHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientDisconnectedHandler> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task HandleClientDisconnectedAsync(MqttServerClientDisconnectedEventArgs eventArgs)
{
try
{
var pythonEventArgs = new PythonDictionary
{
{ "client_id", eventArgs.ClientId },
{ "type", PythonConvert.Pythonfy(eventArgs.DisconnectType) }
};
_pythonScriptHostService.InvokeOptionalFunction("on_client_disconnected", pythonEventArgs);
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while handling client disconnected event.");
}
return Task.CompletedTask;
}
}
}
@@ -0,0 +1,41 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttClientSubscribedTopicHandler : IMqttServerClientSubscribedTopicHandler
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger _logger;
public MqttClientSubscribedTopicHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientSubscribedTopicHandler> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task HandleClientSubscribedTopicAsync(MqttServerClientSubscribedTopicEventArgs eventArgs)
{
try
{
var pythonEventArgs = new PythonDictionary
{
{ "client_id", eventArgs.ClientId },
{ "topic", eventArgs.TopicFilter.Topic },
{ "qos", (int)eventArgs.TopicFilter.QualityOfServiceLevel }
};
_pythonScriptHostService.InvokeOptionalFunction("on_client_subscribed_topic", pythonEventArgs);
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while handling client subscribed topic event.");
}
return Task.CompletedTask;
}
}
}
@@ -0,0 +1,40 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttClientUnsubscribedTopicHandler : IMqttServerClientUnsubscribedTopicHandler
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger _logger;
public MqttClientUnsubscribedTopicHandler(PythonScriptHostService pythonScriptHostService, ILogger<MqttClientUnsubscribedTopicHandler> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task HandleClientUnsubscribedTopicAsync(MqttServerClientUnsubscribedTopicEventArgs eventArgs)
{
try
{
var pythonEventArgs = new PythonDictionary
{
{ "client_id", eventArgs.ClientId },
{ "topic", eventArgs.TopicFilter }
};
_pythonScriptHostService.InvokeOptionalFunction("on_client_unsubscribed_topic", pythonEventArgs);
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while handling client unsubscribed topic event.");
}
return Task.CompletedTask;
}
}
}
@@ -0,0 +1,47 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Protocol;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttConnectionValidator : IMqttServerConnectionValidator
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger _logger;
public MqttConnectionValidator(PythonScriptHostService pythonScriptHostService, ILogger<MqttConnectionValidator> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task ValidateConnectionAsync(MqttConnectionValidatorContext context)
{
try
{
var pythonContext = new PythonDictionary
{
{ "client_id", context.ClientId },
{ "endpoint", context.Endpoint },
{ "is_secure_connection", context.IsSecureConnection },
{ "username", context.Username },
{ "password", context.Password },
{ "result", PythonConvert.Pythonfy(context.ReturnCode) }
};
_pythonScriptHostService.InvokeOptionalFunction("on_validate_client_connection", pythonContext);
context.ReturnCode = PythonConvert.ParseEnum<MqttConnectReturnCode>((string)pythonContext["result"]);
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while validating client connection.");
}
return Task.CompletedTask;
}
}
}
@@ -0,0 +1,216 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore;
using MQTTnet.Implementations;
using MQTTnet.Protocol;
using MQTTnet.Server.Configuration;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttServerService
{
private readonly ILogger<MqttServerService> _logger;
private readonly SettingsModel _settings;
private readonly MqttApplicationMessageInterceptor _mqttApplicationMessageInterceptor;
private readonly MqttServerStorage _mqttServerStorage;
private readonly MqttClientConnectedHandler _mqttClientConnectedHandler;
private readonly MqttClientDisconnectedHandler _mqttClientDisconnectedHandler;
private readonly MqttClientSubscribedTopicHandler _mqttClientSubscribedTopicHandler;
private readonly MqttClientUnsubscribedTopicHandler _mqttClientUnsubscribedTopicHandler;
private readonly MqttConnectionValidator _mqttConnectionValidator;
private readonly IMqttServer _mqttServer;
private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly MqttWebSocketServerAdapter _webSocketServerAdapter;
public MqttServerService(
SettingsModel settings,
CustomMqttFactory mqttFactory,
MqttClientConnectedHandler mqttClientConnectedHandler,
MqttClientDisconnectedHandler mqttClientDisconnectedHandler,
MqttClientSubscribedTopicHandler mqttClientSubscribedTopicHandler,
MqttClientUnsubscribedTopicHandler mqttClientUnsubscribedTopicHandler,
MqttConnectionValidator mqttConnectionValidator,
MqttSubscriptionInterceptor mqttSubscriptionInterceptor,
MqttApplicationMessageInterceptor mqttApplicationMessageInterceptor,
MqttServerStorage mqttServerStorage,
PythonScriptHostService pythonScriptHostService,
ILogger<MqttServerService> logger)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_mqttClientConnectedHandler = mqttClientConnectedHandler ?? throw new ArgumentNullException(nameof(mqttClientConnectedHandler));
_mqttClientDisconnectedHandler = mqttClientDisconnectedHandler ?? throw new ArgumentNullException(nameof(mqttClientDisconnectedHandler));
_mqttClientSubscribedTopicHandler = mqttClientSubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientSubscribedTopicHandler));
_mqttClientUnsubscribedTopicHandler = mqttClientUnsubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientUnsubscribedTopicHandler));
_mqttConnectionValidator = mqttConnectionValidator ?? throw new ArgumentNullException(nameof(mqttConnectionValidator));
_mqttSubscriptionInterceptor = mqttSubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttSubscriptionInterceptor));
_mqttApplicationMessageInterceptor = mqttApplicationMessageInterceptor ?? throw new ArgumentNullException(nameof(mqttApplicationMessageInterceptor));
_mqttServerStorage = mqttServerStorage ?? throw new ArgumentNullException(nameof(mqttServerStorage));
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_webSocketServerAdapter = new MqttWebSocketServerAdapter(mqttFactory.Logger.CreateChildLogger());
var adapters = new List<IMqttServerAdapter>
{
new MqttTcpServerAdapter(mqttFactory.Logger.CreateChildLogger()),
_webSocketServerAdapter
};
_mqttServer = mqttFactory.CreateMqttServer(adapters);
}
public void Configure()
{
_pythonScriptHostService.RegisterProxyObject("publish", new Action<PythonDictionary>(Publish));
_mqttServerStorage.Configure();
_mqttServer.ClientConnectedHandler = _mqttClientConnectedHandler;
_mqttServer.ClientDisconnectedHandler = _mqttClientDisconnectedHandler;
_mqttServer.ClientSubscribedTopicHandler = _mqttClientSubscribedTopicHandler;
_mqttServer.ClientUnsubscribedTopicHandler = _mqttClientUnsubscribedTopicHandler;
_mqttServer.StartAsync(CreateMqttServerOptions()).GetAwaiter().GetResult();
_logger.LogInformation("MQTT server started.");
}
public Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
{
return _webSocketServerAdapter.RunWebSocketConnectionAsync(webSocket, httpContext);
}
private void Publish(PythonDictionary parameters)
{
try
{
var applicationMessageBuilder = new MqttApplicationMessageBuilder()
.WithTopic((string)parameters.get("topic", null))
.WithRetainFlag((bool)parameters.get("retain", false))
.WithQualityOfServiceLevel((MqttQualityOfServiceLevel)(int)parameters.get("qos", 0));
var payload = parameters.get("payload", null);
byte[] binaryPayload;
if (payload == null)
{
binaryPayload = new byte[0];
}
else if (payload is string stringPayload)
{
binaryPayload = Encoding.UTF8.GetBytes(stringPayload);
}
else if (payload is ByteArray byteArray)
{
binaryPayload = byteArray.ToArray();
}
else if (payload is IEnumerable<int> intArray)
{
binaryPayload = intArray.Select(Convert.ToByte).ToArray();
}
else
{
throw new NotSupportedException("Payload type not supported.");
}
applicationMessageBuilder = applicationMessageBuilder
.WithPayload(binaryPayload);
var applicationMessage = applicationMessageBuilder.Build();
_mqttServer.PublishAsync(applicationMessage).GetAwaiter().GetResult();
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while publishing application message from server.");
}
}
private IMqttServerOptions CreateMqttServerOptions()
{
var options = new MqttServerOptionsBuilder()
.WithMaxPendingMessagesPerClient(_settings.MaxPendingMessagesPerClient)
.WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(_settings.CommunicationTimeout))
.WithConnectionValidator(_mqttConnectionValidator)
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
.WithStorage(_mqttServerStorage);
// Configure unencrypted connections
if (_settings.TcpEndPoint.Enabled)
{
options.WithDefaultEndpoint();
if (_settings.TcpEndPoint.TryReadIPv4(out var address4))
{
options.WithDefaultEndpointBoundIPAddress(address4);
}
if (_settings.TcpEndPoint.TryReadIPv6(out var address6))
{
options.WithDefaultEndpointBoundIPV6Address(address6);
}
if (_settings.TcpEndPoint.Port > 0)
{
options.WithDefaultEndpointPort(_settings.TcpEndPoint.Port);
}
}
else
{
options.WithoutDefaultEndpoint();
}
// Configure encrypted connections
if (_settings.EncryptedTcpEndPoint.Enabled)
{
options
.WithEncryptedEndpoint()
.WithEncryptionSslProtocol(SslProtocols.Tls12)
.WithEncryptionCertificate(_settings.EncryptedTcpEndPoint.ReadCertificate());
if (_settings.EncryptedTcpEndPoint.TryReadIPv4(out var address4))
{
options.WithEncryptedEndpointBoundIPAddress(address4);
}
if (_settings.EncryptedTcpEndPoint.TryReadIPv6(out var address6))
{
options.WithEncryptedEndpointBoundIPV6Address(address6);
}
if (_settings.EncryptedTcpEndPoint.Port > 0)
{
options.WithEncryptedEndpointPort(_settings.EncryptedTcpEndPoint.Port);
}
}
else
{
options.WithoutEncryptedEndpoint();
}
if (_settings.ConnectionBacklog > 0)
{
options.WithConnectionBacklog(_settings.ConnectionBacklog);
}
if (_settings.EnablePersistentSessions)
{
options.WithPersistentSessions();
}
return options.Build();
}
}
}
@@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MQTTnet.Server.Configuration;
using Newtonsoft.Json;
namespace MQTTnet.Server.Mqtt
{
public class MqttServerStorage : IMqttServerStorage
{
private readonly List<MqttApplicationMessage> _messages = new List<MqttApplicationMessage>();
private readonly SettingsModel _settings;
private readonly ILogger<MqttServerStorage> _logger;
private string _filename;
private bool _messagesHaveChanged;
public MqttServerStorage(SettingsModel settings, ILogger<MqttServerStorage> logger)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void Configure()
{
if (_settings.RetainedApplicationMessages?.Persist != true)
{
_logger.LogInformation("Persisting of retained application messages is disabled.");
return;
}
_filename = ExpandFilename();
// The retained application messages are stored in a separate thread.
// This is mandatory because writing them to a slow storage (like RaspberryPi SD card)
// will slow down the whole message processing speed.
Task.Run(SaveRetainedMessagesInternalAsync, CancellationToken.None);
}
public Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages)
{
lock (_messages)
{
_messages.Clear();
_messages.AddRange(messages);
_messagesHaveChanged = true;
}
return Task.CompletedTask;
}
private async Task SaveRetainedMessagesInternalAsync()
{
while (true)
{
try
{
await Task.Delay(TimeSpan.FromSeconds(_settings.RetainedApplicationMessages.WriteInterval)).ConfigureAwait(false);
List<MqttApplicationMessage> messages;
lock (_messages)
{
if (!_messagesHaveChanged)
{
continue;
}
messages = new List<MqttApplicationMessage>(_messages);
_messagesHaveChanged = false;
}
var json = JsonConvert.SerializeObject(messages);
await File.WriteAllTextAsync(_filename, json, Encoding.UTF8).ConfigureAwait(false);
_logger.LogInformation($"{messages.Count} retained MQTT messages written.");
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while writing retained MQTT messages.");
}
}
}
public async Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync()
{
if (_settings.RetainedApplicationMessages?.Persist != true)
{
return null;
}
if (!File.Exists(_filename))
{
return null;
}
try
{
var json = await File.ReadAllTextAsync(_filename).ConfigureAwait(false);
var applicationMessages = JsonConvert.DeserializeObject<List<MqttApplicationMessage>>(json);
_logger.LogInformation($"{applicationMessages.Count} retained MQTT messages loaded.");
return applicationMessages;
}
catch (Exception exception)
{
_logger.LogWarning(exception, "Error while loading persisted retained application messages.");
return null;
}
}
private string ExpandFilename()
{
var filename = _settings.RetainedApplicationMessages.Filename;
var uri = new Uri(filename, UriKind.RelativeOrAbsolute);
if (!uri.IsAbsoluteUri)
{
filename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filename);
}
return filename;
}
}
}
@@ -0,0 +1,47 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttSubscriptionInterceptor : IMqttServerSubscriptionInterceptor
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger _logger;
public MqttSubscriptionInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttSubscriptionInterceptor> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task InterceptSubscriptionAsync(MqttSubscriptionInterceptorContext context)
{
try
{
var pythonContext = new PythonDictionary
{
{ "accept_subscription", context.AcceptSubscription },
{ "close_connection", context.CloseConnection },
{ "client_id", context.ClientId },
{ "topic", context.TopicFilter.Topic },
{ "qos", (int)context.TopicFilter.QualityOfServiceLevel }
};
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_subscription", pythonContext);
context.AcceptSubscription = (bool)pythonContext["accept_subscription"];
context.CloseConnection = (bool)pythonContext["close_connection"];
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while intercepting subscription.");
}
return Task.CompletedTask;
}
}
}
+67
View File
@@ -0,0 +1,67 @@
using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace MQTTnet.Server
{
public static class Program
{
public static int Main(string[] args)
{
try
{
PrintLogo();
CreateWebHostBuilder(args).Build().Run();
return 0;
}
catch (Exception exception)
{
Console.WriteLine(exception);
return -1;
}
}
private static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseStartup<Startup>();
private static void PrintLogo()
{
Console.ResetColor();
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(@"
__ __ ____ _______ _______ _ _____
| \/ |/ __ \__ __|__ __| | | / ____|
| \ / | | | | | | | |_ __ ___| |_ | (___ ___ _ ____ _____ _ __
| |\/| | | | | | | | | '_ \ / _ \ __| \___ \ / _ \ '__\ \ / / _ \ '__|
| | | | |__| | | | | | | | | __/ |_ ____) | __/ | \ V / __/ |
|_| |_|\___\_\ |_| |_|_| |_|\___|\__| |_____/ \___|_| \_/ \___|_|");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(@"
-- The official MQTT server implementation of MQTTnet --
Copyright (c) 2017-2019 The MQTTnet Team");
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(@"
https://github.com/chkr1011/MQTTnet");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(@"
Version: 1.0.0-alpha1
License: MIT (read LICENSE file)
Sponsoring: https://opencollective.com/mqttnet
Support: https://github.com/chkr1011/MQTTnet/issues
Docs: https://github.com/chkr1011/MQTTnet/wiki/MQTTnetServer
");
Console.BackgroundColor = ConsoleColor.White;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(" ! THIS IS AN ALPHA VERSION! IT IS NOT RECOMMENDED TO USE IT FOR ANY DIFFERENT PURPOSE THAN TESTING OR EVALUATING!");
Console.WriteLine();
Console.ResetColor();
}
}
}
@@ -0,0 +1 @@
[{"Topic":"a","Payload":"YWFzYXNhc2E=","QualityOfServiceLevel":0,"Retain":true,"UserProperties":[],"ContentType":null,"ResponseTopic":null,"PayloadFormatIndicator":null,"MessageExpiryInterval":null,"TopicAlias":null,"CorrelationData":null,"SubscriptionIdentifier":null},{"Topic":"fgdfgd","Payload":"YWFzYXNhc2E=","QualityOfServiceLevel":0,"Retain":true,"UserProperties":[],"ContentType":null,"ResponseTopic":null,"PayloadFormatIndicator":null,"MessageExpiryInterval":null,"TopicAlias":null,"CorrelationData":null,"SubscriptionIdentifier":null}]
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Server.Scripting.DataSharing
{
public class DataSharingService
{
private readonly Dictionary<string, object> _storage = new Dictionary<string, object>();
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger<DataSharingService> _logger;
public DataSharingService(PythonScriptHostService pythonScriptHostService, ILogger<DataSharingService> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void Configure()
{
_pythonScriptHostService.RegisterProxyObject("write_shared_data", new Action<string, object>(Write));
_pythonScriptHostService.RegisterProxyObject("read_shared_data", new Func<string, object, object>(Read));
}
public void Write(string key, object value)
{
lock (_storage)
{
_storage[key] = value;
_logger.LogInformation($"Shared data with key '{key}' updated.");
}
}
public object Read(string key, object defaultValue)
{
lock (_storage)
{
if (!_storage.TryGetValue(key, out var value))
{
return defaultValue;
}
return value;
}
}
}
}
@@ -0,0 +1,88 @@
using System;
using System.Collections;
using System.Text;
using IronPython.Runtime;
namespace MQTTnet.Server.Scripting
{
public static class PythonConvert
{
public static object ToPython(object value)
{
if (value is PythonDictionary)
{
return value;
}
if (value is string)
{
return value;
}
if (value is int)
{
return value;
}
if (value is float)
{
return value;
}
if (value is bool)
{
return value;
}
if (value is IDictionary dictionary)
{
var pythonDictionary = new PythonDictionary();
foreach (DictionaryEntry dictionaryEntry in dictionary)
{
pythonDictionary.Add(dictionaryEntry.Key, ToPython(dictionaryEntry.Value));
}
return pythonDictionary;
}
if (value is IEnumerable enumerable)
{
var pythonList = new List();
foreach (var item in enumerable)
{
pythonList.Add(ToPython(item));
}
return pythonList;
}
return value;
}
public static string Pythonfy(Enum value)
{
return Pythonfy(value.ToString());
}
public static string Pythonfy(string value)
{
var result = new StringBuilder();
foreach (var @char in value)
{
if (char.IsUpper(@char) && result.Length > 0)
{
result.Append('_');
}
result.Append(char.ToLowerInvariant(@char));
}
return result.ToString();
}
public static TEnum ParseEnum<TEnum>(string value) where TEnum : Enum
{
return (TEnum)Enum.Parse(typeof(TEnum), value.Replace("_", string.Empty), true);
}
}
}
@@ -0,0 +1,66 @@
using System;
using System.IO;
using System.Text;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Server.Scripting
{
public class PythonIOStream : Stream
{
private readonly ILogger _logger;
private readonly Encoding _encoder = Encoding.UTF8;
public PythonIOStream(ILogger<PythonIOStream> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public override void Flush()
{
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (count == 0)
{
return;
}
var text = _encoder.GetString(buffer, offset, count);
if (text.Equals(Environment.NewLine))
{
return;
}
_logger.LogDebug(text);
}
public override bool CanRead { get; } = false;
public override bool CanSeek { get; } = false;
public override bool CanWrite { get; } = true;
public override long Length { get; } = 0L;
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
}
}
@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
namespace MQTTnet.Server.Scripting
{
public class PythonScriptHostService
{
private readonly IDictionary<string, object> _proxyObjects = new ExpandoObject();
private readonly List<PythonScriptInstance> _scriptInstances = new List<PythonScriptInstance>();
private readonly ILogger<PythonScriptHostService> _logger;
private readonly ScriptEngine _scriptEngine;
public PythonScriptHostService(PythonIOStream pythonIOStream, ILogger<PythonScriptHostService> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_scriptEngine = IronPython.Hosting.Python.CreateEngine();
_scriptEngine.Runtime.IO.SetOutput(pythonIOStream, Encoding.UTF8);
}
public void Configure()
{
AddSearchPaths(_scriptEngine);
var scriptsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Scripts");
foreach (var filename in Directory.GetFiles(scriptsDirectory, "*.py", SearchOption.AllDirectories).OrderBy(file => file))
{
TryInitializeScript(filename);
}
}
public void RegisterProxyObject(string name, object @object)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (@object == null) throw new ArgumentNullException(nameof(@object));
_proxyObjects.Add(name, @object);
}
public void InvokeOptionalFunction(string name, object parameters)
{
if (name == null) throw new ArgumentNullException(nameof(name));
lock (_scriptInstances)
{
foreach (var pythonScriptInstance in _scriptInstances)
{
try
{
pythonScriptInstance.InvokeOptionalFunction(name, parameters);
}
catch (Exception exception)
{
_logger.LogError(exception, $"Error while invoking function '{name}' at script '{pythonScriptInstance.Name}'.");
}
}
}
}
private void TryInitializeScript(string filename)
{
try
{
var scriptName = new FileInfo(filename).Name;
_logger.LogTrace($"Initializing Python script '{scriptName}'...");
var code = File.ReadAllText(filename);
var scriptInstance = CreateScriptInstance(scriptName, code);
scriptInstance.InvokeOptionalFunction("initialize");
_scriptInstances.Add(scriptInstance);
_logger.LogInformation($"Initialized script '{scriptName}'.");
}
catch (Exception exception)
{
_logger.LogError(exception, $"Error while initializing script '{new FileInfo(filename).Name}'.");
}
}
private PythonScriptInstance CreateScriptInstance(string name, string code)
{
var scriptScope = _scriptEngine.CreateScope();
var source = scriptScope.Engine.CreateScriptSourceFromString(code, SourceCodeKind.File);
var compiledCode = source.Compile();
scriptScope.SetVariable("mqtt_net_server", _proxyObjects);
compiledCode.Execute(scriptScope);
return new PythonScriptInstance(name, scriptScope);
}
private void AddSearchPaths(ScriptEngine scriptEngine)
{
var paths = new List<string>
{
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Lib"),
"/usr/lib/python2.7",
@"C:\Python27\Lib"
};
AddSearchPaths(scriptEngine, paths);
}
private void AddSearchPaths(ScriptEngine scriptEngine, IEnumerable<string> paths)
{
var searchPaths = scriptEngine.GetSearchPaths();
foreach (var path in paths)
{
if (Directory.Exists(path))
{
searchPaths.Add(path);
_logger.LogInformation($"Added Python lib path: {path}");
}
}
scriptEngine.SetSearchPaths(searchPaths);
}
}
}
@@ -0,0 +1,57 @@
using System;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
namespace MQTTnet.Server.Scripting
{
public class PythonScriptInstance
{
private readonly ScriptScope _scriptScope;
public PythonScriptInstance(string name, ScriptScope scriptScope)
{
_scriptScope = scriptScope;
Name = name;
}
public string Name { get; }
public bool InvokeOptionalFunction(string name, params object[] parameters)
{
return InvokeOptionalFunction(name, parameters, out _);
}
public bool InvokeOptionalFunction(string name, object[] parameters, out object result)
{
if (name == null) throw new ArgumentNullException(nameof(name));
lock (_scriptScope)
{
if (!_scriptScope.Engine.Operations.TryGetMember(_scriptScope, name, out var member))
{
result = null;
return false;
}
if (!(member is PythonFunction function))
{
throw new Exception($"Member '{name}' is no Python function.");
}
try
{
result = _scriptScope.Engine.Operations.Invoke(function, parameters);
return true;
}
catch (Exception exception)
{
var details = _scriptScope.Engine.GetService<ExceptionOperations>().FormatException(exception);
var message = $"Error while invoking function '{name}'. " + Environment.NewLine + details;
throw new Exception(message, exception);
}
}
}
}
}
+107
View File
@@ -0,0 +1,107 @@
import json
def initialize():
"""
This function is invoked after the script file has been loaded.
It will be executed only one time.
"""
print("Hello World from Sample script.")
def on_validate_client_connection(context):
"""
This function is invoked whenever a client wants to connect. It can be used to validate the connection.
"""
print(context)
mqtt_net_server.write_shared_data(context["client_id"], {"custom_value_1": 1, "custom_value_2": True})
return
if context["client_id"] != "test_client":
context["result"] = "connection_refused_not_authorized"
return
if context["username"] != "bud spencer":
context["result"] = "connection_refused_not_authorized"
return
if context["password"] != "secret":
context["result"] = "connection_refused_not_authorized"
print(context)
def on_intercept_subscription(context):
"""
This function is invoked whenever a client wants to subscribe to a topic.
"""
print("Client '{client_id}' want's to subscribe to topic '{topic}'.".format(client_id=context["client_id"], topic=context["topic"]))
def on_intercept_application_message(context):
"""
This function is invoked for every processed application message. It also allows modifying
the message or cancel processing at all.
"""
client_id = context["client_id"]
if client_id != None:
shared_data = mqtt_net_server.read_shared_data(context["client_id"], {})
print(shared_data)
if context["topic"] == "topic_with_response":
json_payload = {
"hello": "world",
"x": 1,
"y": True,
"z": None
}
application_message = {
"retain": False,
"topic": "reply",
"payload": json.dumps(json_payload)
}
mqtt_net_server.publish(application_message)
print("Client '{client_id}' published topic '{topic}'.".format(client_id=context["client_id"], topic=context["topic"]))
def on_client_connected(event_args):
"""
This function is called whenever a client has passed the validation is connected.
"""
print("Client '{client_id}' is now connected.".format(client_id=event_args["client_id"]))
def on_client_disconnected(event_args):
"""
This function is called whenever a client has disconnected.
"""
print("Client '{client_id}' is now disconnected (type = {type}).".format(client_id=event_args["client_id"], type=event_args["type"]))
def on_client_subscribed_topic(event_args):
"""
This function is called whenever a client has subscribed to a topic (when allowed).
"""
print("Client '{client_id}' has subscribed to '{topic}'.".format(client_id=event_args["client_id"], topic=event_args["topic"]))
def on_client_unsubscribed_topic(event_args):
"""
This function is called whenever a client has unsubscribed from a topic.
"""
print("Client '{client_id}' has unsubscribed from '{topic}'.".format(client_id=event_args["client_id"], topic=event_args["topic"]))
+19
View File
@@ -0,0 +1,19 @@
This directory contains scripts which are loaded by the server and can be used to perform the following tasks.
1. Validation of client connections via credentials, client IDs etc.
2. Manipulation of every processed message.
3. Validation of subscription attempts.
4. Publishing of custom application messages.
All scripts are loaded and _MQTTnet Server_ will invoke functions according to predefined naming conventions.
If a function is implemented in multiple scripts the context will be moved throug all instances. This allows overriding of results or passing data to other (following) scripts.
The Python starndard library ships with _MQTTnet Server_. But it is possible to add custom paths with Python libraries.
```
import sys
sys.path.append(PATH_TO_LIBRARY)
```
* All scripts must have the file extension _.py_.
* All scripts are sorted alphabetically (A to Z) before being loaded and parsed.
+142
View File
@@ -0,0 +1,142 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Scripting.Utils;
using MQTTnet.Server.Configuration;
using MQTTnet.Server.Logging;
using MQTTnet.Server.Mqtt;
using MQTTnet.Server.Scripting;
using MQTTnet.Server.Scripting.DataSharing;
namespace MQTTnet.Server
{
public class Startup
{
public Startup(IConfiguration configuration)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void Configure(
IApplicationBuilder application,
IHostingEnvironment environment,
MqttServerService mqttServerService,
PythonScriptHostService pythonScriptHostService,
DataSharingService dataSharingService,
SettingsModel settings)
{
if (environment.IsDevelopment())
{
application.UseDeveloperExceptionPage();
}
else
{
application.UseHsts();
}
application.UseStaticFiles();
application.UseHttpsRedirection();
application.UseMvc();
ConfigureWebSocketEndpoint(application, mqttServerService, settings);
dataSharingService.Configure();
pythonScriptHostService.Configure();
mqttServerService.Configure();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSingleton(ReadSettings());
services.AddSingleton<PythonIOStream>();
services.AddSingleton<PythonScriptHostService>();
services.AddSingleton<DataSharingService>();
services.AddSingleton<MqttNetLoggerWrapper>();
services.AddSingleton<CustomMqttFactory>();
services.AddSingleton<MqttServerService>();
services.AddSingleton<MqttServerStorage>();
services.AddSingleton<MqttClientConnectedHandler>();
services.AddSingleton<MqttClientDisconnectedHandler>();
services.AddSingleton<MqttClientSubscribedTopicHandler>();
services.AddSingleton<MqttClientUnsubscribedTopicHandler>();
services.AddSingleton<MqttConnectionValidator>();
services.AddSingleton<MqttSubscriptionInterceptor>();
services.AddSingleton<MqttApplicationMessageInterceptor>();
}
private SettingsModel ReadSettings()
{
var settings = new Configuration.SettingsModel();
Configuration.Bind("MQTT", settings);
return settings;
}
private static void ConfigureWebSocketEndpoint(
IApplicationBuilder application,
MqttServerService mqttServerService,
SettingsModel settings)
{
if (settings?.WebSocketEndPoint?.Enabled != true)
{
return;
}
if (string.IsNullOrEmpty(settings.WebSocketEndPoint.Path))
{
return;
}
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromSeconds(settings.WebSocketEndPoint.KeepAliveInterval),
ReceiveBufferSize = settings.WebSocketEndPoint.ReceiveBufferSize
};
if (settings.WebSocketEndPoint.AllowedOrigins?.Any() == true)
{
webSocketOptions.AllowedOrigins.AddRange(settings.WebSocketEndPoint.AllowedOrigins);
}
application.UseWebSockets(webSocketOptions);
application.Use(async (context, next) =>
{
if (context.Request.Path == settings.WebSocketEndPoint.Path)
{
if (context.WebSockets.IsWebSocketRequest)
{
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false))
{
await mqttServerService.RunWebSocketConnectionAsync(webSocket, context).ConfigureAwait(false);
}
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next().ConfigureAwait(false);
}
});
}
}
}
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
+58
View File
@@ -0,0 +1,58 @@
{
"Kestrel": {
"EndPoints": {
"Http": {
"Url": "http://localhost:80"
},
"Https": {
"Url": "http://localhost:443"
}
}
},
"MQTT": {
/*
Wildcard Addresses:
* - All local IP addresses
localhost - Localhost only
disable - Skip address assignment
*/
"TcpEndPoint": {
"Enabled": true,
"IPv4": "localhost",
"IPv6": "localhost",
"Port": 1883
},
"EncryptedTcpEndPoint": {
"Enabled": false,
"IPv4": "localhost",
"IPv6": "localhost",
"Port": 8883,
"CertificatePath": "/absolute/path/to/pfx"
},
"WebSocketEndPoint": {
"Enabled": true,
"Path": "/mqtt",
"KeepAliveInterval": 120, // In seconds.
"ReceiveBufferSize": 4096,
"AllowedOrigins": [] // List of strings with URLs.
},
"CommunicationTimeout": 15, // In seconds.
"ConnectionBacklog": 0, /* Set 0 to disable */
"EnablePersistentSessions": false,
"MaxPendingMessagesPerClient": 250,
"RetainedApplicationMessages": {
"Persist": true,
"Filename": "RetainedApplicationMessages.json",
"WriteInterval": 10 // In seconds.
},
"EnableDebugLogging": false
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"AllowedHosts": "*"
}
+12
View File
@@ -0,0 +1,12 @@
{
"version": "1.0.0",
"name": "mqtt.test",
"private": true,
"devDependencies": {},
"dependencies": {
"@types/node": "^8.0.46",
"mqtt": "^2.15.1",
"systemjs": "^0.20.19",
"typescript": "^2.3.4"
}
}
+2
View File
@@ -0,0 +1,2 @@
@echo off
START "MQTTnet Server" dotnet .\MQTTnet.Server.dll
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
echo "Starting MQTTnet Server.."
dotnet .\MQTTnet.Server.dll
+12
View File
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"target": "es5"
},
"exclude": [
"node_modules"
]
}
+35
View File
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>MQTTnet Server test</title>
</head>
<body>
<script src="node_modules/systemjs/dist/system.js"></script>
<script>
System.config({
packages: {
'': {
defaultExtension: 'js'
}
},
map: {
mqtt: 'node_modules/mqtt/dist/mqtt.min'
}
});
System.import('app/app');
</script>
<div>
<p id="state"></p>
<input id="topic" placeholder="topic" />
<input id="msg" placeholder="msg" />
<button id="publish">send</button>
</div>
<div>
<ul id="msgs" ></ul>
</div>
</body>
</html>
+58
View File
@@ -0,0 +1,58 @@
import { connect } from "mqtt";
var client = connect('ws://' + location.host + '/mqtt',
{
clientId: "client" + Math.floor(Math.random() * 6) + 1
});
window.onbeforeunload = () => {
client.end();
};
var publishButton = document.getElementById("publish");
var topicInput = <HTMLInputElement>document.getElementById("topic");
var msgInput = <HTMLInputElement>document.getElementById("msg");
var stateParagraph = document.getElementById("state");
var msgsList = <HTMLUListElement>document.getElementById("msgs");
publishButton.onclick = click => {
var topic = topicInput.value;
var msg = msgInput.value;
client.publish(topic, msg);
};
client.on('connect', () => {
client.subscribe('#', { qos: 0 }, (err, granted) => {
console.log(err);
});
client.publish('presence', 'Hello mqtt');
stateParagraph.innerText = "connected";
showMsg("[connect]");
});
client.on("error", e => {
showMsg("error: " + e.message);
});
client.on("reconnect", () => {
stateParagraph.innerText = "reconnecting";
showMsg("[reconnect]");
});
client.on('message', (topic, message) => {
showMsg(topic + ": " + message.toString());
});
function showMsg(msg: string) {
//console.log(msg);
var node = document.createElement("LI");
node.appendChild(document.createTextNode(msg));
msgsList.appendChild(node);
if (msgsList.childElementCount > 50) {
msgsList.removeChild(msgsList.childNodes[0]);
}
}
@@ -1,8 +1,8 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.Adapter
{
@@ -10,8 +10,10 @@ namespace MQTTnet.Adapter
{
string Endpoint { get; }
IMqttPacketSerializer PacketSerializer { get; }
bool IsSecureConnection { get; }
MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
event EventHandler ReadingPacketStarted;
event EventHandler ReadingPacketCompleted;
@@ -20,7 +22,7 @@ namespace MQTTnet.Adapter
Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken);
Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken);
Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken);
}
@@ -1,7 +1,7 @@
using MQTTnet.Adapter;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
namespace MQTTnet.Client
namespace MQTTnet.Adapter
{
public interface IMqttClientAdapterFactory
{
+1 -1
View File
@@ -6,7 +6,7 @@ namespace MQTTnet.Adapter
{
public interface IMqttServerAdapter : IDisposable
{
event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
Action<MqttServerAdapterClientAcceptedEventArgs> ClientAcceptedHandler { get; set; }
Task StartAsync(IMqttServerOptions options);
Task StopAsync();
+74 -36
View File
@@ -7,9 +7,9 @@ using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Serializer;
namespace MQTTnet.Adapter
{
@@ -22,26 +22,29 @@ namespace MQTTnet.Adapter
private readonly IMqttNetChildLogger _logger;
private readonly IMqttChannel _channel;
private readonly MqttPacketReader _packetReader;
private readonly byte[] _fixedHeaderBuffer = new byte[2];
private readonly byte[] _singleByteBuffer = new byte[1];
private bool _isDisposed;
public MqttChannelAdapter(IMqttChannel channel, IMqttPacketSerializer serializer, IMqttNetChildLogger logger)
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetChildLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
_packetReader = new MqttPacketReader(_channel);
_logger = logger.CreateChildLogger(nameof(MqttChannelAdapter));
}
public string Endpoint => _channel.Endpoint;
public IMqttPacketSerializer PacketSerializer { get; }
public bool IsSecureConnection => _channel.IsSecureConnection;
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public event EventHandler ReadingPacketStarted;
public event EventHandler ReadingPacketCompleted;
@@ -52,11 +55,14 @@ namespace MQTTnet.Adapter
try
{
_logger.Verbose("Connecting [Timeout={0}]", timeout);
await Internal.TaskExtensions
.TimeoutAfterAsync(ct => _channel.ConnectAsync(ct), timeout, cancellationToken)
.ConfigureAwait(false);
if (timeout == TimeSpan.Zero)
{
await _channel.ConnectAsync(cancellationToken).ConfigureAwait(false);
}
else
{
await MqttTaskTimeout.WaitAsync(t => _channel.ConnectAsync(t), timeout, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception exception)
{
@@ -75,11 +81,15 @@ namespace MQTTnet.Adapter
try
{
_logger.Verbose("Disconnecting [Timeout={0}]", timeout);
await Internal.TaskExtensions
.TimeoutAfterAsync(ct => _channel.DisconnectAsync(), timeout, cancellationToken)
.ConfigureAwait(false);
if (timeout == TimeSpan.Zero)
{
await _channel.DisconnectAsync(cancellationToken).ConfigureAwait(false);
}
else
{
await MqttTaskTimeout.WaitAsync(
t => _channel.DisconnectAsync(t), timeout, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception exception)
{
@@ -91,17 +101,27 @@ namespace MQTTnet.Adapter
WrapException(exception);
}
}
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
{
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var packetData = PacketSerializer.Serialize(packet);
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
PacketSerializer.FreeBuffer();
var packetData = PacketFormatterAdapter.Encode(packet);
_logger.Verbose("TX >>> {0}", packet);
if (timeout == TimeSpan.Zero)
{
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
}
else
{
await MqttTaskTimeout.WaitAsync(
t => _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, t), timeout, cancellationToken).ConfigureAwait(false);
}
PacketFormatterAdapter.FreeBuffer();
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
}
catch (Exception exception)
{
@@ -125,14 +145,13 @@ namespace MQTTnet.Adapter
try
{
ReceivedMqttPacket receivedMqttPacket;
if (timeout > TimeSpan.Zero)
if (timeout == TimeSpan.Zero)
{
receivedMqttPacket = await Internal.TaskExtensions.TimeoutAfterAsync(ReceiveAsync, timeout, cancellationToken).ConfigureAwait(false);
receivedMqttPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
}
else
{
receivedMqttPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
receivedMqttPacket = await MqttTaskTimeout.WaitAsync(ReceiveAsync, timeout, cancellationToken).ConfigureAwait(false);
}
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
@@ -140,13 +159,18 @@ namespace MQTTnet.Adapter
return null;
}
var packet = PacketSerializer.Deserialize(receivedMqttPacket);
if (!PacketFormatterAdapter.ProtocolVersion.HasValue)
{
PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket);
}
var packet = PacketFormatterAdapter.Decode(receivedMqttPacket);
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
}
_logger.Verbose("RX <<< {0}", packet);
_logger.Verbose("RX ({0} bytes) <<< {1}", receivedMqttPacket.TotalLength, packet);
return packet;
}
@@ -168,15 +192,21 @@ namespace MQTTnet.Adapter
private async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
{
var fixedHeader = await MqttPacketReader.ReadFixedHeaderAsync(_channel, _fixedHeaderBuffer, _singleByteBuffer, cancellationToken).ConfigureAwait(false);
var readFixedHeaderResult = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false);
try
{
if (readFixedHeaderResult.ConnectionClosed)
{
return null;
}
ReadingPacketStarted?.Invoke(this, EventArgs.Empty);
var fixedHeader = readFixedHeaderResult.FixedHeader;
if (fixedHeader.RemainingLength == 0)
{
return new ReceivedMqttPacket(fixedHeader.Flags, null);
return new ReceivedMqttPacket(fixedHeader.Flags, null, 2);
}
var body = new byte[fixedHeader.RemainingLength];
@@ -192,20 +222,28 @@ namespace MQTTnet.Adapter
}
#if WINDOWS_UWP
var readBytes = await _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false);
var readBytes = await _channel.ReadAsync(body, bodyOffset, (int)chunkSize, cancellationToken).ConfigureAwait(false);
#else
// async/await is not used to avoid the overhead of context switches. We assume that the reamining data
// async/await is not used to avoid the overhead of context switches. We assume that the remaining data
// has been sent from the sender directly after the initial bytes.
var readBytes = _channel.ReadAsync(body, bodyOffset, chunkSize, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
cancellationToken.ThrowIfCancellationRequested();
ExceptionHelper.ThrowIfGracefulSocketClose(readBytes);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
if (readBytes == 0)
{
return null;
}
bodyOffset += readBytes;
} while (bodyOffset < body.Length);
return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(body, 0, body.Length));
var bodyReader = new MqttPacketBodyReader(body, 0, body.Length);
return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength);
}
finally
{
@@ -1,16 +1,16 @@
using MQTTnet.Exceptions;
using MQTTnet.Protocol;
using MQTTnet.Client.Connecting;
using MQTTnet.Exceptions;
namespace MQTTnet.Adapter
{
public class MqttConnectingFailedException : MqttCommunicationException
{
public MqttConnectingFailedException(MqttConnectReturnCode returnCode)
: base($"Connecting with MQTT server failed ({returnCode}).")
public MqttConnectingFailedException(MqttClientConnectResultCode resultCode)
: base($"Connecting with MQTT server failed ({resultCode.ToString()}).")
{
ReturnCode = returnCode;
ResultCode = resultCode;
}
public MqttConnectReturnCode ReturnCode { get; }
public MqttClientConnectResultCode ResultCode { get; }
}
}
+5 -2
View File
@@ -1,17 +1,20 @@
using MQTTnet.Serializer;
using MQTTnet.Formatter;
namespace MQTTnet.Adapter
{
public class ReceivedMqttPacket
{
public ReceivedMqttPacket(byte fixedHeader, MqttPacketBodyReader body)
public ReceivedMqttPacket(byte fixedHeader, MqttPacketBodyReader body, int totalLength)
{
FixedHeader = fixedHeader;
Body = body;
TotalLength = totalLength;
}
public byte FixedHeader { get; }
public MqttPacketBodyReader Body { get; }
public int TotalLength { get; }
}
}
@@ -1,80 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Protocol;
namespace MQTTnet
{
public static class ApplicationMessagePublisherExtensions
{
public static async Task PublishAsync(this IApplicationMessagePublisher publisher, IEnumerable<MqttApplicationMessage> applicationMessages)
{
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
foreach (var applicationMessage in applicationMessages)
{
await publisher.PublishAsync(applicationMessage).ConfigureAwait(false);
}
}
public static async Task PublishAsync(this IApplicationMessagePublisher publisher, params MqttApplicationMessage[] applicationMessages)
{
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
foreach (var applicationMessage in applicationMessages)
{
await publisher.PublishAsync(applicationMessage).ConfigureAwait(false);
}
}
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic)
{
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return publisher.PublishAsync(builder => builder
.WithTopic(topic));
}
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload)
{
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return publisher.PublishAsync(builder => builder
.WithTopic(topic)
.WithPayload(payload));
}
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return publisher.PublishAsync(builder => builder
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel(qualityOfServiceLevel));
}
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
{
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
if (topic == null) throw new ArgumentNullException(nameof(topic));
return publisher.PublishAsync(builder => builder
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel(qualityOfServiceLevel)
.WithRetainFlag(retain));
}
public static Task PublishAsync(this IApplicationMessagePublisher publisher, Func<MqttApplicationMessageBuilder, MqttApplicationMessageBuilder> builder)
{
var message = builder(new MqttApplicationMessageBuilder()).Build();
return publisher.PublishAsync(message);
}
}
}
+2 -1
View File
@@ -7,9 +7,10 @@ namespace MQTTnet.Channel
public interface IMqttChannel : IDisposable
{
string Endpoint { get; }
bool IsSecureConnection { get; }
Task ConnectAsync(CancellationToken cancellationToken);
Task DisconnectAsync();
Task DisconnectAsync(CancellationToken cancellationToken);
Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace MQTTnet.Client.Connecting
{
public interface IMqttClientConnectedHandler
{
Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs);
}
}
@@ -0,0 +1,9 @@
namespace MQTTnet.Client.Connecting
{
public class MqttClientAuthenticateResult
{
public bool IsSessionPresent { get; set; }
public MqttClientConnectResultCode ResultCode { get; set; }
}
}
@@ -0,0 +1,28 @@
namespace MQTTnet.Client.Connecting
{
public enum MqttClientConnectResultCode
{
Success = 0,
UnspecifiedError = 128,
MalformedPacket = 129,
ProtocolError = 130,
ImplementationSpecificError = 131,
UnsupportedProtocolVersion = 132,
ClientIdentifierNotValid = 133,
BadUserNameOrPassword = 134,
NotAuthorized = 135,
ServerUnavailable = 136,
ServerBusy = 137,
Banned = 138,
BadAuthenticationMethod = 140,
TopicNameInvalid = 144,
PacketTooLarge = 149,
QuotaExceeded = 151,
PayloadFormatInvalid = 153,
RetainNotSupported = 154,
QoSNotSupported = 155,
UseAnotherServer = 156,
ServerMoved = 157,
ConnectionRateExceeded = 159
}
}
@@ -0,0 +1,14 @@
using System;
namespace MQTTnet.Client.Connecting
{
public class MqttClientConnectedEventArgs : EventArgs
{
public MqttClientConnectedEventArgs(MqttClientAuthenticateResult authenticateResult)
{
AuthenticateResult = authenticateResult ?? throw new ArgumentNullException(nameof(authenticateResult));
}
public MqttClientAuthenticateResult AuthenticateResult { get; }
}
}
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
namespace MQTTnet.Client.Connecting
{
public class MqttClientConnectedHandlerDelegate : IMqttClientConnectedHandler
{
private readonly Func<MqttClientConnectedEventArgs, Task> _handler;
public MqttClientConnectedHandlerDelegate(Action<MqttClientConnectedEventArgs> handler)
{
if (handler == null) throw new ArgumentNullException(nameof(handler));
_handler = context =>
{
handler(context);
return Task.FromResult(0);
};
}
public MqttClientConnectedHandlerDelegate(Func<MqttClientConnectedEventArgs, Task> handler)
{
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
}
public Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs)
{
return _handler(eventArgs);
}
}
}
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace MQTTnet.Client.Disconnecting
{
public interface IMqttClientDisconnectedHandler
{
Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs);
}
}
@@ -0,0 +1,9 @@
namespace MQTTnet.Client.Disconnecting
{
public class MqttClientDisconnectOptions
{
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
public string ReasonString { get; set; }
}
}
@@ -0,0 +1,8 @@
namespace MQTTnet.Client.Disconnecting
{
public enum MqttClientDisconnectReason
{
NormalDisconnection = 0,
}
}
@@ -1,6 +1,6 @@
using System;
namespace MQTTnet.Client
namespace MQTTnet.Client.Disconnecting
{
public class MqttClientDisconnectedEventArgs : EventArgs
{
@@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
namespace MQTTnet.Client.Disconnecting
{
public class MqttClientDisconnectedHandlerDelegate : IMqttClientDisconnectedHandler
{
private readonly Func<MqttClientDisconnectedEventArgs, Task> _handler;
public MqttClientDisconnectedHandlerDelegate(Action<MqttClientDisconnectedEventArgs> handler)
{
if (handler == null) throw new ArgumentNullException(nameof(handler));
_handler = context =>
{
handler(context);
return Task.FromResult(0);
};
}
public MqttClientDisconnectedHandlerDelegate(Func<MqttClientDisconnectedEventArgs, Task> handler)
{
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
}
public Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs)
{
return _handler(eventArgs);
}
}
}
+14 -8
View File
@@ -1,6 +1,11 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Subscribing;
using MQTTnet.Client.Unsubscribing;
namespace MQTTnet.Client
{
@@ -9,13 +14,14 @@ namespace MQTTnet.Client
bool IsConnected { get; }
IMqttClientOptions Options { get; }
event EventHandler<MqttClientConnectedEventArgs> Connected;
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
IMqttClientConnectedHandler ConnectedHandler { get; set; }
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
Task<MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken);
Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken);
Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options);
Task DisconnectAsync();
Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
Task UnsubscribeAsync(IEnumerable<string> topics);
Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken);
Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken);
}
}
+2 -1
View File
@@ -1,4 +1,5 @@
using MQTTnet.Diagnostics;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
namespace MQTTnet.Client
{
+261 -196
View File
@@ -1,13 +1,18 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Publishing;
using MQTTnet.Client.Receiving;
using MQTTnet.Client.Subscribing;
using MQTTnet.Client.Unsubscribing;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.PacketDispatcher;
using MQTTnet.Packets;
using MQTTnet.Protocol;
@@ -16,16 +21,17 @@ namespace MQTTnet.Client
public class MqttClient : IMqttClient
{
private readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
private readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
private readonly Stopwatch _sendTracker = new Stopwatch();
private readonly object _disconnectLock = new object();
private readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
private readonly IMqttClientAdapterFactory _adapterFactory;
private readonly IMqttNetChildLogger _logger;
private CancellationTokenSource _cancellationTokenSource;
internal Task _packetReceiverTask;
internal Task _keepAliveMessageSenderTask;
private CancellationTokenSource _backgroundCancellationTokenSource;
private Task _packetReceiverTask;
private Task _keepAlivePacketsSenderTask;
private IMqttChannelAdapter _adapter;
private bool _cleanDisconnectInitiated;
private long _disconnectGate;
@@ -38,14 +44,17 @@ namespace MQTTnet.Client
_logger = logger.CreateChildLogger(nameof(MqttClient));
}
public event EventHandler<MqttClientConnectedEventArgs> Connected;
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public IMqttClientConnectedHandler ConnectedHandler { get; set; }
public IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; }
public bool IsConnected { get; private set; }
public IMqttClientOptions Options { get; private set; }
public async Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options)
public async Task<MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options.ChannelOptions == null) throw new ArgumentException("ChannelOptions are not set.");
@@ -59,31 +68,39 @@ namespace MQTTnet.Client
_packetIdentifierProvider.Reset();
_packetDispatcher.Reset();
_cancellationTokenSource = new CancellationTokenSource();
_disconnectGate = 0;
_adapter = _adapterFactory.CreateClientAdapter(options, _logger);
_backgroundCancellationTokenSource = new CancellationTokenSource();
var backgroundCancellationToken = _backgroundCancellationTokenSource.Token;
_logger.Verbose($"Trying to connect with server ({Options.ChannelOptions}).");
await _adapter.ConnectAsync(Options.CommunicationTimeout, _cancellationTokenSource.Token).ConfigureAwait(false);
_disconnectGate = 0;
var adapter = _adapterFactory.CreateClientAdapter(options, _logger);
_adapter = adapter;
_logger.Verbose($"Trying to connect with server '{options.ChannelOptions}' (Timeout={options.CommunicationTimeout}).");
await _adapter.ConnectAsync(options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
_logger.Verbose("Connection with server established.");
StartReceivingPackets(_cancellationTokenSource.Token);
_packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken);
var connectResponse = await AuthenticateAsync(options.WillMessage, _cancellationTokenSource.Token).ConfigureAwait(false);
_logger.Verbose("MQTT connection with server established.");
var authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, cancellationToken).ConfigureAwait(false);
_sendTracker.Restart();
if (Options.KeepAlivePeriod != TimeSpan.Zero)
{
StartSendingKeepAliveMessages(_cancellationTokenSource.Token);
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken);
}
IsConnected = true;
Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResponse.IsSessionPresent));
_logger.Info("Connected.");
return new MqttClientConnectResult(connectResponse.IsSessionPresent);
var connectedHandler = ConnectedHandler;
if (connectedHandler != null)
{
await connectedHandler.HandleConnectedAsync(new MqttClientConnectedEventArgs(authenticateResult)).ConfigureAwait(false);
}
return authenticateResult;
}
catch (Exception exception)
{
@@ -98,15 +115,16 @@ namespace MQTTnet.Client
}
}
public async Task DisconnectAsync()
public async Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken)
{
try
{
_cleanDisconnectInitiated = true;
if (IsConnected && !_cancellationTokenSource.IsCancellationRequested)
if (IsConnected)
{
await SendAsync(new MqttDisconnectPacket(), _cancellationTokenSource.Token).ConfigureAwait(false);
var disconnectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(options);
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
}
}
finally
@@ -118,64 +136,55 @@ namespace MQTTnet.Client
}
}
public async Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
public async Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
if (options == null) throw new ArgumentNullException(nameof(options));
ThrowIfNotConnected();
var subscribePacket = new MqttSubscribePacket
{
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(),
TopicFilters = topicFilters.ToList()
};
var subscribePacket = _adapter.PacketFormatterAdapter.DataConverter.CreateSubscribePacket(options);
subscribePacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
var response = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, _cancellationTokenSource.Token).ConfigureAwait(false);
if (response.SubscribeReturnCodes.Count != subscribePacket.TopicFilters.Count)
{
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
}
return subscribePacket.TopicFilters.Select((t, i) => new MqttSubscribeResult(t, response.SubscribeReturnCodes[i])).ToList();
var subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientSubscribeResult(subscribePacket, subAckPacket);
}
public Task UnsubscribeAsync(IEnumerable<string> topicFilters)
public async Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
if (options == null) throw new ArgumentNullException(nameof(options));
ThrowIfNotConnected();
var unsubscribePacket = new MqttUnsubscribePacket
{
PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier(),
TopicFilters = topicFilters.ToList()
};
var unsubscribePacket = _adapter.PacketFormatterAdapter.DataConverter.CreateUnsubscribePacket(options);
unsubscribePacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
return SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, _cancellationTokenSource.Token);
var unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientUnsubscribeResult(unsubscribePacket, unsubAckPacket);
}
public Task PublishAsync(MqttApplicationMessage applicationMessage)
public Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
MqttTopicValidator.ThrowIfInvalid(applicationMessage.Topic);
ThrowIfNotConnected();
var publishPacket = applicationMessage.ToPublishPacket();
var publishPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePublishPacket(applicationMessage);
switch (applicationMessage.QualityOfServiceLevel)
{
case MqttQualityOfServiceLevel.AtMostOnce:
{
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
return SendAsync(publishPacket, _cancellationTokenSource.Token);
return PublishAtMostOnce(publishPacket, cancellationToken);
}
case MqttQualityOfServiceLevel.AtLeastOnce:
{
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
return SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, _cancellationTokenSource.Token);
return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
}
case MqttQualityOfServiceLevel.ExactlyOnce:
{
return PublishExactlyOnce(publishPacket, _cancellationTokenSource.Token);
return PublishExactlyOnceAsync(publishPacket, cancellationToken);
}
default:
{
@@ -186,33 +195,31 @@ namespace MQTTnet.Client
public void Dispose()
{
_cancellationTokenSource?.Cancel(false);
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
_backgroundCancellationTokenSource?.Cancel(false);
_backgroundCancellationTokenSource?.Dispose();
_backgroundCancellationTokenSource = null;
_adapter?.Dispose();
_adapter = null;
}
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
private async Task<MqttClientAuthenticateResult> AuthenticateAsync(IMqttChannelAdapter channelAdapter, MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
{
var connectPacket = new MqttConnectPacket
{
ClientId = Options.ClientId,
Username = Options.Credentials?.Username,
Password = Options.Credentials?.Password,
CleanSession = Options.CleanSession,
KeepAlivePeriod = (ushort)Options.KeepAlivePeriod.TotalSeconds,
WillMessage = willApplicationMessage
};
var connectPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket(
willApplicationMessage,
Options);
var response = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
if (response.ConnectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
var result = channelAdapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket);
if (result.ResultCode != MqttClientConnectResultCode.Success)
{
throw new MqttConnectingFailedException(response.ConnectReturnCode);
throw new MqttConnectingFailedException(result.ResultCode);
}
return response;
_logger.Verbose("Authenticated MQTT connection with server established.");
return result;
}
private void ThrowIfNotConnected()
@@ -230,19 +237,20 @@ namespace MQTTnet.Client
var clientWasConnected = IsConnected;
InitiateDisconnect();
IsConnected = false;
try
{
if (_adapter != null)
{
_logger.Verbose("Disconnecting [Timeout={0}]", Options.CommunicationTimeout);
await _adapter.DisconnectAsync(Options.CommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
}
await WaitForTaskAsync(_packetReceiverTask, sender).ConfigureAwait(false);
await WaitForTaskAsync(_keepAliveMessageSenderTask, sender).ConfigureAwait(false);
await WaitForTaskAsync(_keepAlivePacketsSenderTask, sender).ConfigureAwait(false);
_logger.Verbose("Disconnected from adapter.");
}
catch (Exception adapterException)
@@ -255,7 +263,12 @@ namespace MQTTnet.Client
_cleanDisconnectInitiated = false;
_logger.Info("Disconnected.");
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected, exception));
var disconnectedHandler = DisconnectedHandler;
if (disconnectedHandler != null)
{
await disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception)).ConfigureAwait(false);
}
}
}
@@ -265,12 +278,12 @@ namespace MQTTnet.Client
{
try
{
if (_cancellationTokenSource?.IsCancellationRequested == true)
if (_backgroundCancellationTokenSource?.IsCancellationRequested == true)
{
return;
}
_cancellationTokenSource?.Cancel(false);
_backgroundCancellationTokenSource?.Cancel(false);
}
catch (Exception exception)
{
@@ -281,11 +294,14 @@ namespace MQTTnet.Client
private Task SendAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (cancellationToken.IsCancellationRequested)
{
return Task.FromResult(0);
}
_sendTracker.Restart();
return _adapter.SendPacketAsync(packet, cancellationToken);
return _adapter.SendPacketAsync(packet, Options.CommunicationTimeout, cancellationToken);
}
private async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttBasePacket
@@ -300,26 +316,22 @@ namespace MQTTnet.Client
identifier = packetWithIdentifier.PacketIdentifier.Value;
}
var packetAwaiter = _packetDispatcher.AddPacketAwaiter<TResponsePacket>(identifier);
try
using (var packetAwaiter = _packetDispatcher.AddPacketAwaiter<TResponsePacket>(identifier))
{
await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false);
var respone = await Internal.TaskExtensions.TimeoutAfterAsync(ct => packetAwaiter.Task, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
return (TResponsePacket)respone;
}
catch (MqttCommunicationTimedOutException)
{
_logger.Warning(null, "Timeout while waiting for packet of type '{0}'.", typeof(TResponsePacket).Namespace);
throw;
}
finally
{
_packetDispatcher.RemovePacketAwaiter<TResponsePacket>(identifier);
try
{
await _adapter.SendPacketAsync(requestPacket, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
return await packetAwaiter.WaitOneAsync(Options.CommunicationTimeout).ConfigureAwait(false);
}
catch (MqttCommunicationTimedOutException)
{
_logger.Warning(null, "Timeout while waiting for packet of type '{0}'.", typeof(TResponsePacket).Namespace);
throw;
}
}
}
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken)
private async Task TrySendKeepAliveMessagesAsync(CancellationToken cancellationToken)
{
try
{
@@ -364,7 +376,7 @@ namespace MQTTnet.Client
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_keepAliveMessageSenderTask, exception).ConfigureAwait(false);
await DisconnectInternalAsync(_keepAlivePacketsSenderTask, exception).ConfigureAwait(false);
}
}
finally
@@ -373,7 +385,7 @@ namespace MQTTnet.Client
}
}
private async Task ReceivePacketsAsync(CancellationToken cancellationToken)
private async Task TryReceivePacketsAsync(CancellationToken cancellationToken)
{
try
{
@@ -381,13 +393,24 @@ namespace MQTTnet.Client
while (!cancellationToken.IsCancellationRequested)
{
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken)
.ConfigureAwait(false);
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
if (packet != null && !cancellationToken.IsCancellationRequested)
if (cancellationToken.IsCancellationRequested)
{
await ProcessReceivedPacketAsync(packet, cancellationToken).ConfigureAwait(false);
return;
}
if (packet == null)
{
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, null).ConfigureAwait(false);
}
return;
}
await TryProcessReceivedPacketAsync(packet, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception exception)
@@ -422,103 +445,99 @@ namespace MQTTnet.Client
}
}
private Task ProcessReceivedPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
if (packet is MqttPublishPacket publishPacket)
{
return ProcessReceivedPublishPacketAsync(publishPacket, cancellationToken);
}
if (packet is MqttPingReqPacket)
{
return SendAsync(new MqttPingRespPacket(), cancellationToken);
}
if (packet is MqttDisconnectPacket)
{
return DisconnectAsync();
}
if (packet is MqttPubRelPacket pubRelPacket)
{
return ProcessReceivedPubRelPacket(pubRelPacket, cancellationToken);
}
_packetDispatcher.Dispatch(packet);
return Task.FromResult(0);
}
private Task ProcessReceivedPublishPacketAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{
FireApplicationMessageReceivedEvent(publishPacket);
return Task.FromResult(0);
}
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
FireApplicationMessageReceivedEvent(publishPacket);
return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }, cancellationToken);
}
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
// QoS 2 is implement as method "B" (4.3.3 QoS 2: Exactly once delivery)
FireApplicationMessageReceivedEvent(publishPacket);
return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }, cancellationToken);
}
throw new MqttCommunicationException("Received a not supported QoS level.");
}
private Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
{
var response = new MqttPubCompPacket
{
PacketIdentifier = pubRelPacket.PacketIdentifier
};
return SendAsync(response, cancellationToken);
}
private async Task PublishExactlyOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
var pubRelPacket = new MqttPubRelPacket
{
PacketIdentifier = pubRecPacket.PacketIdentifier
};
await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
}
private void StartReceivingPackets(CancellationToken cancellationToken)
{
_packetReceiverTask = Task.Factory.StartNew(
() => ReceivePacketsAsync(cancellationToken),
cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default).Unwrap();
}
private void StartSendingKeepAliveMessages(CancellationToken cancellationToken)
{
_keepAliveMessageSenderTask = Task.Factory.StartNew(
() => SendKeepAliveMessagesAsync(cancellationToken),
cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default).Unwrap();
}
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
private async Task TryProcessReceivedPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
try
{
var applicationMessage = publishPacket.ToApplicationMessage();
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage));
if (packet is MqttPublishPacket publishPacket)
{
await TryProcessReceivedPublishPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false);
}
else if (packet is MqttPubRelPacket pubRelPacket)
{
await SendAsync(new MqttPubCompPacket
{
PacketIdentifier = pubRelPacket.PacketIdentifier,
ReasonCode = MqttPubCompReasonCode.Success
}, cancellationToken).ConfigureAwait(false);
}
else if (packet is MqttPingReqPacket)
{
await SendAsync(new MqttPingRespPacket(), cancellationToken).ConfigureAwait(false);
}
else if (packet is MqttDisconnectPacket)
{
await DisconnectAsync(null, cancellationToken).ConfigureAwait(false);
}
else
{
_packetDispatcher.Dispatch(packet);
}
}
catch (Exception exception)
{
if (_cleanDisconnectInitiated)
{
return;
}
if (exception is OperationCanceledException)
{
}
else if (exception is MqttCommunicationException)
{
_logger.Warning(exception, "MQTT communication exception while receiving packets.");
}
else
{
_logger.Error(exception, "Unhandled exception while receiving packets.");
}
_packetDispatcher.Dispatch(exception);
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
}
}
}
private async Task TryProcessReceivedPublishPacketAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
try
{
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{
await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
if (await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false))
{
await SendAsync(new MqttPubAckPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubAckReasonCode.Success
}, cancellationToken).ConfigureAwait(false);
}
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
if (await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false))
{
var pubRecPacket = new MqttPubRecPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRecReasonCode.Success
};
await SendAsync(pubRecPacket, cancellationToken).ConfigureAwait(false);
}
}
else
{
throw new MqttProtocolViolationException("Received a not supported QoS level.");
}
}
catch (Exception exception)
{
@@ -526,6 +545,52 @@ namespace MQTTnet.Client
}
}
private async Task<MqttClientPublishResult> PublishAtMostOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
// 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);
}
private 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);
}
private async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
var pubRelPacket = new MqttPubRelPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRelReasonCode.Success
};
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(pubRecPacket, pubCompPacket);
}
private async Task<bool> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
{
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
var handler = ApplicationMessageReceivedHandler;
if (handler != null)
{
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage);
await handler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
return !eventArgs.ProcessingFailed;
}
return true;
}
private static async Task WaitForTaskAsync(Task task, Task sender)
{
if (task == sender || task == null)
@@ -1,12 +0,0 @@
namespace MQTTnet.Client
{
public class MqttClientConnectResult
{
public MqttClientConnectResult(bool isSessionPresent)
{
IsSessionPresent = isSessionPresent;
}
public bool IsSessionPresent { get; }
}
}
@@ -1,14 +0,0 @@
using System;
namespace MQTTnet.Client
{
public class MqttClientConnectedEventArgs : EventArgs
{
public MqttClientConnectedEventArgs(bool isSessionPresent)
{
IsSessionPresent = isSessionPresent;
}
public bool IsSessionPresent { get; }
}
}

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