Compare commits

...
137 Commits
Author SHA1 Message Date
Christian Kratky 4b08bb41f5 Refactor code. 2020-04-10 10:53:17 +02:00
Christian Kratky c89918a890 Update python runtime for MQTTnet.Server. 2020-04-08 22:01:20 +02:00
Christian 6546175ea2 Update README.md 2020-04-07 12:22:16 +02:00
Christian Kratky b9e9fe64ac Refactor adapter handling. 2020-04-05 15:43:16 +02:00
Christian 9fd7c7bc1e Merge pull request #891 from PSanetra/async-message-processing
Introduce asynchronous message processing
2020-04-04 23:36:58 +02:00
PSanetra fcd6b7ff52 Process messages async
This commit fixes issues which were caused by processing messages synchronously in the packet receiver loop. This blocked KeepAlive and Ack packets from being processed while a message was processed.

Fixes #648
Fixes #587
Fixes #829
2020-04-04 22:40:16 +02:00
Christian Kratky 54870adc64 Fix build warnings and errors. 2020-04-04 19:40:09 +02:00
PSanetra 892e4842ed Fix MQTTnet.Tests/Server_Tests/Same_Client_Id_Connect_Disconnect_Event_Order
This failing test was caused by a raise condition in the MqttClientSessionsManager
The test failed as the value of `flow` in the assertion `Assert.AreEqual("cdc", flow);` was "ccd"
2020-04-04 15:03:15 +02:00
Christian Kratky fc358f9015 Fix build errors. 2020-04-04 13:08:29 +02:00
Christian Kratky b93cd5b673 Refactor code. 2020-04-04 13:07:10 +02:00
Christian aafc99f39e Merge pull request #884 from chkr1011/feature-isconnected
Added check if the server is started or not.
2020-04-04 13:05:58 +02:00
Christian b14d7affe5 Merge branch 'master' into feature-isconnected 2020-04-04 13:05:45 +02:00
Christian Kratky 67d56644b2 Expose custom session items in session status. 2020-04-04 12:54:33 +02:00
Christian Kratky 6e0d3ec0fb Update nuget packages. 2020-04-04 12:15:51 +02:00
Christian Kratky 2c6613a7b7 Merge branch 'feature/low_level_client' 2020-04-04 01:09:48 +02:00
Christian Kratky d464bfb7c5 Fix connect and disconnect event flow. 2020-04-04 01:01:37 +02:00
Christian Kratky 7a24970035 Fix UnitTests. 2020-04-03 23:08:54 +02:00
Christian Kratky db906eca24 Merge branch 'master' into feature/low_level_client 2020-04-03 22:53:22 +02:00
Christian Kratky ad128c7889 Improve socket handling. 2020-04-03 22:52:55 +02:00
Christian 3e096bc353 Merge pull request #886 from PMExtra/feature-userproperties
GetUserProperty extension.
2020-04-03 22:50:52 +02:00
HansM f0be8404cf Merge branch 'master' into feature-userproperties 2020-04-03 18:49:39 +02:00
Christian 6de28d32e4 Merge pull request #889 from PMExtra/feature-with-uri
Support MqttClientOptionsBuilder.WithConnectionUri()
2020-04-03 14:32:11 +02:00
PM Extra 2ede7df38a Merge branch 'master' into feature-userproperties 2020-04-03 18:04:00 +08:00
PM Extra 2a35108a73 Merge branch 'master' into feature-with-uri 2020-04-03 18:03:11 +08:00
PMExtra 774d49a908 Fix exception message and add test cases. 2020-04-03 15:03:10 +08:00
PMExtra c83bec6e81 Fix for Nullable. 2020-04-03 14:44:04 +08:00
PMExtra 763d9778bb Check parameters and add friendly exceptions. 2020-04-02 17:29:24 +08:00
PMExtra 9368eebfd5 Update nuspec. 2020-04-02 16:07:21 +08:00
Christian Kratky 81b547f386 Fix logging issues. 2020-04-01 23:15:08 +02:00
Christian e33c5ccb4c Merge pull request #885 from chkr1011/fix-userproperties
Removed checks for user properties in MQTT v3 and instead ignore them.
2020-04-01 23:02:08 +02:00
Christian Kratky 0ca4c88e69 Merge branch 'master' into feature/low_level_client 2020-04-01 23:01:03 +02:00
Christian Kratky 04ccdcd562 Add low level client. 2020-04-01 20:43:00 +02:00
PMExtra aafd4ea658 Fix. 2020-04-01 15:30:24 +08:00
PMExtra 4eb65fc3d2 Support MqttClientOptionsBuilder.WithConnectionUri() 2020-03-31 16:42:04 +08:00
PMExtra f7f00f4e06 Update nuspec. 2020-03-31 12:03:00 +08:00
HansM af0b98eace Merge pull request #888 from chkr1011/fix-copyright-date
Adjusted copyright information in the nuget package specifications.
2020-03-30 19:47:46 +02:00
SeppPenner 0da2ab9de6 Adjusted copyright information in the nuget package specifications. 2020-03-30 19:46:18 +02:00
SeppPenner ea22ef4164 Adjusted .nuspec file, added property to interface as well. 2020-03-30 19:44:15 +02:00
SeppPenner cb5a0cf38b Updated nuget package description. 2020-03-30 19:38:44 +02:00
PMExtra 55dc940abb GetUserProperty extension. 2020-03-30 17:10:04 +08:00
SeppPenner c40e3c64aa Removed checks for user properties in MQTT v3 and instead ignore them. 2020-03-29 17:26:40 +02:00
SeppPenner 87c4fa14ee Added check if the server is started or not. 2020-03-29 16:48:19 +02:00
Christian Kratky 0653b89701 Fix broken unit tests. 2020-03-26 22:33:30 +01:00
Christian Kratky 561474c572 Fix broken unit tests. 2020-03-26 15:35:14 +01:00
Christian dca2af47d8 Merge pull request #880 from xljiulang/master
Reduce the performance cost of Memory<T>.getSpan()
2020-03-25 21:33:34 +01:00
xljiulang 9ed80daf83 Reduce the performance cost of Memory<T>.getSpan() 2020-03-25 21:32:43 +08:00
Christian 0d39bbbc0c Merge pull request #870 from JanEggers/aspnetcore3bugfixes
Aspnetcore3bugfixes
2020-03-17 10:17:12 +01:00
JanEggers 52faf6d76a updated releasenotes 2020-03-15 14:47:50 +01:00
JanEggers 20eda96921 fixed core 3.1 startup and verified it still works with 2.1 and net461 2020-03-15 14:43:59 +01:00
Christian Kratky f6a814c6d7 Update build script. 2020-03-14 23:52:07 +01:00
Christian Kratky 8e0bce7734 Remove obsolete references. 2020-03-14 23:49:26 +01:00
Christian Kratky 72fa13ca5b Fix build errors. 2020-03-14 22:59:17 +01:00
Christian Kratky 45e3455e6c Merge remote-tracking branch 'origin/master' 2020-03-14 22:46:23 +01:00
Christian 160da00f55 Merge pull request #873 from andrei-m-code/bugfix/mqttnet-client-semaphore-dispose
DIspose semaphore in MqttChannelAdapter
2020-03-14 22:39:46 +01:00
Christian Kratky 6b4b829cf6 Merge branch 'develop' 2020-03-14 22:38:36 +01:00
Andrei Mikhalevich e7fa465616 DIspose semaphore in MqttChannelAdapter 2020-03-12 11:07:30 -04:00
Christian 799005803e Merge pull request #869 from dominikviererbe/feature/MqttClientUnsubscribeOptionsBuilder
Added MqttClientUnsubscribeOptionsBuilder
2020-03-10 09:55:57 +01:00
Dominik Viererbe a20c845a5a Update MQTTnet.nuspec 2020-03-09 18:27:35 +01:00
Dominik Viererbe 5f59aca62c Added MqttClientUnsubscribeOptionsBuilder analogous to MqttClientSubscribeOptionsBuilder 2020-02-25 16:30:08 +01:00
HansM 76ddf8c503 Merge pull request #852 from cdobler/check-"Keep-Alive"-while-other-messages-are-sent-#851
Added receiveTracker to detect whether messages are received from broker.
2020-02-24 17:02:42 +01:00
Christoph 122034296c Merge branch 'develop' into check-"Keep-Alive"-while-other-messages-are-sent-#851 2020-02-24 08:20:25 +01:00
Christian 6f71aaf1ce Merge pull request #868 from wazzamatazz/master
Add .NET 3.1 target
2020-02-22 19:32:12 +01:00
Graham Watts fd8caed9af Add .NET 3.1 target
Add .NET Core 3.1 target that uses ASP.NET Core framework reference instead of NuGet package references
2020-02-22 11:25:01 +02:00
Christoph 313f90a526 Added receiveTracker to detect whether messages are received from broker. 2020-01-28 14:36:19 +01:00
Christian Kratky 4d27f42e72 Merge remote-tracking branch 'origin/develop' into develop 2020-01-25 23:34:53 +01:00
Christian 8ae318fd5c Merge pull request #838 from jimsch/connectErrors
Allow access to full packet on failed connect
2020-01-12 22:11:28 +01:00
Jim Schaad 7dde2f1f4c Change variable name per review request
Change the name of the variable from requestCode to request
2020-01-12 12:31:09 -08:00
Christian Kratky 49c5278ba0 Merge branch 'master' into develop 2020-01-10 22:18:48 +01:00
Christian Kratky ce51d12a03 Update SDK versions. 2020-01-10 22:16:31 +01:00
Jim Schaad b1559a8fa5 Correct to print right result
Lost a change while moving from my developement branch to here.
2020-01-08 11:11:41 -08:00
Jim Schaad 457143c840 Merge branch 'develop' into connectErrors 2020-01-08 11:10:58 -08:00
Christian c60bf51b1e Merge pull request #839 from JanEggers/develop
bugfixes
2020-01-08 19:14:08 +01:00
JanEggers 630400da53 avoid task.run on newer platforms 2020-01-04 11:55:12 +01:00
JanEggers a152066ed3 fixed managed client so it does not send disconnect packet when disposed 2020-01-04 11:54:41 +01:00
JanEggers 138cd16c8d fixed compilation 2020-01-04 11:53:15 +01:00
JanEggers 27a61819d3 removed unused code 2020-01-04 11:52:43 +01:00
Jim Schaad 9c378fb088 Provide the full ACK on a connection failure
There are some systems that return information on a failed exception and thus the client wants access to the full set of ACK information.  Specifically the user properties.
2019-12-31 14:44:25 -08:00
Jim Schaad d4088420cf Return correct set of user properties
Return the response not the request set of user properties
2019-12-31 14:33:31 -08:00
Christian 2e7d074174 Merge pull request #833 from JanEggers/bugfixes
Bugfixes
2019-12-28 20:28:38 +01:00
JanEggers 328ffc734f dont allow publish before the client is started and adjusted testclient accordingly 2019-12-28 15:55:35 +01:00
Christian 7b25c6f274 Merge pull request #836 from PMExtra/develop
Support existing session with ManagedMqttClient.
2019-12-28 14:04:27 +01:00
PMExtra 069c19aab9 Support existing session with ManagedMqttClient. 2019-12-23 20:38:29 +08:00
Christian 35e729bd7b Merge pull request #826 from fogzot/develop-iretainedmesagesmanager
Added IMqttRetainedMessagesManager
2019-12-22 16:50:37 +01:00
JanEggers ac06a001e2 fixed some warnings 2019-12-21 10:42:18 +01:00
JanEggers a77e0b8dde spread dispose pattern 2019-12-21 09:09:03 +01:00
JanEggers 698f0b66d2 fixed object disposed exception on netfx fixing #803 #733 #728 #486 #158 2019-12-20 15:37:42 +01:00
JanEggers 14419e4d63 fixed clients overlapping from one test to another by:
- adding client and server wrappers to verify the clientid starts with the testname
- adding disposable baseclass
- clear disconnect handler from client so it does not try to reconnect after it is disposed
- moved code from dispose to cleanup in case it is called in a stop method as dispose may only be called once while start / stop may be called multiple times
2019-12-20 15:37:23 +01:00
Federico Di Gregorio 3600ebbc62 Create default MqttRetainedMessagesManager in options 2019-12-18 10:25:24 +01:00
Federico Di Gregorio 55676965d9 Use IList in IMqttRetainedMessagesManager return values 2019-12-17 12:39:57 +01:00
Federico Di Gregorio f949d54aef Added IMqttRetainedMessagesManager
* Added interface IMqttRetainedMessagesManager to allow for different
retained messages manager implementations. The interface copies almost
exactly the current MqttRetainedMessagesManager implementation.

* Added IMqttRetainedMessagesManager.Start() to configure the provided
IMqttRetainedMessagesManager outside the constructor. This method returns
Task because some implementations that use external storage may be slow on
initialization (e.g., connect to a database).

* Modified MqttRetainedMessagesManager to implement new interface.
2019-12-17 11:57:17 +01:00
Christian Kratky 624fafc662 Update nugets. 2019-12-08 20:10:46 +01:00
Christian 23e93212a4 Merge pull request #800 from stepkillah/develop
Replace raw bytes with x509 certificate to allow specify passwords and flags #2
2019-11-27 21:33:21 +01:00
Anton Yaroshenko f9b2c91ddb merged 2019-11-18 11:32:00 +02:00
Anton Yaroshenko 9fb1cc3320 added UWP compiler flags 2019-11-18 11:16:34 +02:00
Christian Kratky 1c89a8bc75 Update docs. 2019-11-17 21:25:30 +01:00
Christian Kratky 8be9ce8024 Merge remote-tracking branch 'origin/develop' into develop 2019-11-17 21:25:05 +01:00
Christian 002ba27fd3 Merge pull request #799 from chkr1011/revert-798-master
Revert "Replace raw bytes with x509 certificate to allow specify passwords and flags"
2019-11-17 21:24:47 +01:00
Christian 9aaa94e821 Revert "Replace raw bytes with x509 certificate to allow specify passwords and flags" 2019-11-17 21:24:30 +01:00
Anton Yaroshenko e8ff48bfd3 Revert "blind uwp fix"
This reverts commit ac326babdf.
2019-11-17 21:23:20 +01:00
Anton Yaroshenko 5db05f186f Revert "replace raw byte with x509 certificate to allow specify passwords"
This reverts commit a38623cb5a.
2019-11-17 21:23:16 +01:00
Christian b2c2209b4f Update MQTTnet.nuspec 2019-11-17 21:06:37 +01:00
Christian c490e83c91 Merge pull request #798 from stepkillah/master
Replace raw bytes with x509 certificate to allow specify passwords and flags
2019-11-17 21:05:05 +01:00
Anton Yaroshenko ac326babdf blind uwp fix 2019-11-17 20:12:03 +02:00
Anton Yaroshenko a38623cb5a replace raw byte with x509 certificate to allow specify passwords 2019-11-17 19:42:23 +02:00
Christian Kratky a63489fa5e Update docs. 2019-11-14 20:52:35 +01:00
Christian Kratky 4b7409240b Merge remote-tracking branch 'origin/develop' into develop 2019-11-14 20:47:36 +01:00
Christian Kratky 896e9ed0aa Add interceptor for unsubscriptions. 2019-11-14 20:47:33 +01:00
Christian 0950dfbb8b Merge pull request #786 from llrosa/debugFixClientId
Fix for #785 - Skip client disconnection process if unauthorized
2019-11-03 13:23:23 +01:00
Lucas Rosa 4105beb7e6 Some more cleaning up: 2019-10-29 12:55:57 -03:00
Lucas Rosa 5c25905510 Clean up 2019-10-29 12:48:04 -03:00
Lucas Rosa e3f6b579b2 Working PoC 2019-10-29 12:32:20 -03:00
Lucas Rosa c467c9c818 Debugging duplicated client id issue 2019-10-29 11:44:41 -03:00
Christian Kratky 78b57531b6 Update docs. 2019-10-24 20:09:11 +02:00
Christian 84d9f533bd Merge pull request #782 from cstichlberger/develop
Reduce subscription publishing loglevel in ManagedMqttClient
2019-10-24 20:06:47 +02:00
Christoph Stichlberger 7479e320ee Turn subscription publishing loglevel down
- the Info level for publishing subscriptions is quite noisy
 - previously this wasn't a problem because subscription processing was buffered, now the message is logged for nearly every single un/subscription that is made
 - all other frequently recurring log events are also on level Verbose
2019-10-23 17:01:44 +02:00
Christian Kratky 152303dfbd Update docs. 2019-10-18 19:31:40 +02:00
Christian a495668f2b Merge pull request #776 from cstichlberger/branches/fix_managed_client_subscription_issues
Separate current and reconnect subscriptions in managed client
2019-10-18 19:29:09 +02:00
HansM 82b714d4c7 Merge pull request #777 from cstichlberger/develop
Adapt test assembly paths in buildscript
2019-10-18 11:39:06 +02:00
Christoph Stichlberger 44d71b884c Adapt test assembly paths in buildscript 2019-10-17 06:45:46 +02:00
Christoph Stichlberger 781c5d4d7d Separate current and reconnect subscsriptions in managed client
This change kills several birds with one stone:
 - by performing the re-publishing of all subscriptions only at reconnect it fixes issue https://github.com/chkr1011/MQTTnet/issues/569 (retained messages are received again with every new subscription that is added)
 - not sending the subscriptions again with every (un)subscription is also a performance improvement if subscriptions are modified on a regular basis, because only the updated subscriptions are sent to the broker (previously, if you had 100 subscriptions and added a new one, 100 suscriptions were re-sent to the broker along with the new one, causing a significant delay)
 -until now subscriptions were sent to the broker only every ConnectionCheckInterval which caused unnecessary delays, and for request-response patterns could cause reponse messages to be missed due to the subscription delay. Now (un)subscriptions are published immediately

Subscriptions are now cleaned up at logout (after the connection stops being maintained). This is in line with the clearing of the _messageQueue in StopAsync

Explanatory note: the _subscriptionsQueuedSignal could ideally be a ManualResetEvent(Slim) but those do not offer an awaitable Wait method, so a SemaphoreSlim is used. This possibly causes a few empty loops in PublishSubscriptionsAsync due to the semaphore being incremented with every SubscribeAsync call but all subscriptions getting processed in one sweep, but those empty loops shouldn't be a problem
2019-10-15 15:26:05 +02:00
Christian Kratky b175119008 Refactoring and doc updates. 2019-10-04 20:01:51 +02:00
Christian Kratky cfcb858423 Update libs. 2019-10-04 19:51:35 +02:00
Christian Kratky 5fa12649bf Merge remote-tracking branch 'origin/develop' into develop 2019-10-04 19:51:13 +02:00
Christian a9a09f4ecd Merge pull request #754 from jltjohanlindqvist/branches/fixUnhandledTaskCrash
Remove if clause that stopped handling of tasks with exceptions
2019-10-04 19:50:56 +02:00
Christian Kratky 3ff5806dbb Dispose queue in managed client. 2019-10-03 15:26:33 +02:00
Christian Kratky 0ec78cf5cc Merge remote-tracking branch 'origin/develop' into develop 2019-10-03 15:25:02 +02:00
Christian c3193c568a Merge pull request #763 from zawodskoj/master
Fix for #762 - thread pool leak
2019-10-03 15:24:56 +02:00
Christian Kratky a1f02284e9 Merge branch 'master' into develop 2019-10-03 15:23:52 +02:00
Jimmy Rosenskog 699558e47a Added exception handling to make sure all tasks are observed to avoid UnobservedTaskException. 2019-10-03 09:46:40 +02:00
Бессонов Дмитрий 15ff03c136 use C#7.0 for default arguments 2019-09-30 16:10:30 +03:00
Бессонов Дмитрий b12e3bc611 fix for #762 2019-09-30 16:05:23 +03:00
Johan x Lindqvist 61c3e02242 Updated exception message to be in line with other exception messages. 2019-09-19 13:31:57 +02:00
Johan x Lindqvist 5618c4c1f7 Always access the exception property on the task if it has status IsFaulted.
By checking the Exception property it will consider the task exception as handled which means the finalizer won't throw an unhandled task exception.
2019-09-18 13:49:59 +02:00
Johan x Lindqvist ba9ceed7ce Use Task.WhenAll to handle errors in both tasks.
Previously if there was an exception in the first task that is awaited the second task would not be awaited.
2019-09-16 11:33:37 +02:00
Johan x Lindqvist ad1c198e43 Remove if clause that stopped handling of tasks with exceptions.
Now the task is always awaited and thus the exception in the task is handled.
2019-09-12 09:05:08 +02:00
Christian 248e2a5bdd Merge pull request #746 from bcrosnier/pr/mqtt5-assigned-client-id
Better handling of AssignedClientIdentifier - chkr1011/MQTTnet#745
2019-08-25 22:12:57 +02:00
Benjamin Crosnier 1a41415dd1 Allow AssignedClientIdentifier in ClientConnected, ClientDisconnected and ConnectedHandler - Fixes chkr1011/MQTTnet#745 2019-08-19 10:41:28 +02:00
150 changed files with 3722 additions and 1780 deletions
+9 -5
View File
@@ -8,18 +8,19 @@
<license type="file">LICENSE</license>
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<icon>images\Logo_128x128.png</icon>
<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-2019</copyright>
<copyright>Copyright Christian Kratky 2016-2020</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="$nugetVersion" />
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.3" />
<dependency id="Microsoft.AspNetCore.Http.Connections" version="1.0.3" />
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.1.1" />
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.1.1" />
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="3.1.3" />
<dependency id="Microsoft.AspNetCore.Http.Connections" version="1.1.0" />
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.2.1" />
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="3.1.3" />
</dependencies>
</metadata>
@@ -27,6 +28,9 @@
<!-- License -->
<file src="..\LICENSE" />
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
</files>
@@ -8,10 +8,11 @@
<license type="file">LICENSE</license>
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<icon>images\Logo_128x128.png</icon>
<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-2019</copyright>
<copyright>Copyright Christian Kratky 2016-2020</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="$nugetVersion" />
@@ -22,6 +23,9 @@
<!-- License -->
<file src="..\LICENSE" />
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\"/>
+5 -1
View File
@@ -8,10 +8,11 @@
<license type="file">LICENSE</license>
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<icon>images\Logo_128x128.png</icon>
<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-2019</copyright>
<copyright>Copyright Christian Kratky 2016-2020</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="$nugetVersion" />
@@ -22,6 +23,9 @@
<!-- License -->
<file src="..\LICENSE" />
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\"/>
@@ -8,10 +8,11 @@
<license type="file">LICENSE</license>
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<icon>images\Logo_128x128.png</icon>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2019</copyright>
<copyright>Copyright Christian Kratky 2016-2020</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="$nugetVersion" />
@@ -22,6 +23,9 @@
<!-- License -->
<file src="..\LICENSE" />
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard1.3\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard1.3\"/>
+5 -1
View File
@@ -8,10 +8,11 @@
<license type="file">LICENSE</license>
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<icon>images\Logo_128x128.png</icon>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This package contains the .NET Standard version of MQTTnet 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>
<copyright>Copyright Christian Kratky 2016-2020</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">
@@ -33,6 +34,9 @@
<!-- License -->
<file src="..\LICENSE" />
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
+24 -2
View File
@@ -8,13 +8,32 @@
<license type="file">LICENSE</license>
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<icon>images\Logo_128x128.png</icon>
<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) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</description>
<releaseNotes>
* [All] Due to a merge issue not all changes are included in 3.0.8. All these changes are now included in this version.
* [Core] Updated all nuget references.
* [Core] Added MqttApplicationMessage.GetUserProperty() convenience method (thanks to @PMExtra).
* [LowLevelMqttClient] Added low level MQTT client in order to provide more flexibility when using the MQTT protocol. This client requires detailed knowledge about the MQTT protocol.
* [Client] Improve connection stability (thanks to @jltjohanlindqvist).
* [Client] Support WithConnectionUri to configure client (thanks to @PMExtra).
* [Client] Support PublishAsync with QoS 1 and QoS 2 from within an ApplicationMessageReceivedHandler (#648, #587, thanks to @PSanetra).
* [Client] Fixed MqttCommunicationTimedOutExceptions, caused by a long running ApplicationMessageReceivedHandler, which blocked MQTT packets from being processed (#829, thanks to @PSanetra).
* [ManagedClient] Added builder class for MqttClientUnsubscribeOptions (thanks to @dominikviererbe).
* [ManagedClient] Added support for persisted sessions (thansk to @PMExtra).
* [ManagedClient] Fixed a memory leak (thanks to @zawodskoj).
* [ManagedClient] Improved internal subscription management (#569, thanks to @cstichlberger).
* [ManagedClient] Refactored log messages (thanks to @cstichlberger).
* [Server] Added support for assigned client IDs (MQTTv5 only) (thanks to @bcrosnier).
* [Server] Added interceptor for unsubscriptions.
* [Server] Removed exceptions when user properties are set with MQTT protocol version 3.1
* [Server] Added custom session items to the client status.
* [Server] Added option to check whether the server is already started properly or not.
* [MQTTnet.AspNetCore] improved compatibility with AspNetCore 3.1
* [MQTTnet.Server] Added interceptor for unsubscriptions.
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2019</copyright>
<copyright>Copyright Christian Kratky 2016-2020</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">
@@ -30,7 +49,7 @@
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.8" />
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" />
</group>
</dependencies>
</metadata>
@@ -39,6 +58,9 @@
<!-- License -->
<file src="..\LICENSE" />
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
+17 -14
View File
@@ -12,12 +12,16 @@ Write-Host "Nuget version = $nugetVersion"
Write-Host "MSBuild path = $msbuild"
Write-Host
# Build and execute tests
&$msbuild ..\Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp2.1" /verbosity:m
&$msbuild ..\Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp2.1" /verbosity:m
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile "nuget.exe"
vstest.console.exe ..\Tests\MQTTnet.Core.Tests\bin\Release\netcoreapp2.1\MQTTnet.Tests.dll
vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp2.1\MQTTnet.AspNetCore.Tests.dll
.\nuget.exe restore ..\MQTTnet.sln
# Build and execute tests
&$msbuild ..\Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /verbosity:m
&$msbuild ..\Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /verbosity:m
vstest.console.exe ..\Tests\MQTTnet.Core.Tests\bin\Release\netcoreapp3.1\MQTTnet.Tests.dll
vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.Tests.dll
# Build the core library
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
@@ -51,7 +55,6 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp2.1\M
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Create NuGet packages.
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile "nuget.exe"
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
@@ -84,8 +87,8 @@ Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
# Build MQTTnet.Server Portable
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion
$source = "..\Source\MQTTnet.Server\bin\Release\netcoreapp2.2\publish"
$destination = "..\Source\MQTTnet.Server\bin\MQTTnet.Server-Portable-v$nugetVersion.zip"
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Portable-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
@@ -95,8 +98,8 @@ If(Test-path $destination) {Remove-item $destination}
# Build MQTTnet.Server Linux-x64
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-x64
$source = "..\Source\MQTTnet.Server\bin\Release\netcoreapp2.2\linux-x64\publish"
$destination = "..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-x64-v$nugetVersion.zip"
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-x64\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-x64-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
@@ -106,8 +109,8 @@ If(Test-path $destination) {Remove-item $destination}
# Build MQTTnet.Server Linux-ARM
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-arm
$source = "..\Source\MQTTnet.Server\bin\Release\netcoreapp2.2\linux-ARM\publish"
$destination = "..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-ARM-v$nugetVersion.zip"
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-ARM\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-ARM-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
@@ -117,8 +120,8 @@ If(Test-path $destination) {Remove-item $destination}
# Build MQTTnet.Server Windows-x64
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime win-x64
$source = "..\Source\MQTTnet.Server\bin\Release\netcoreapp2.2\win-x64\publish"
$destination = "..\Source\MQTTnet.Server\bin\MQTTnet.Server-Windows-x64-v$nugetVersion.zip"
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\win-x64\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Windows-x64-v$nugetVersion.zip"
If(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
+2 -2
View File
@@ -1,9 +1,9 @@
# Import codeSigningKey.pfx
In order to import the key for code signing on a new developer machine use the following command within the VS developer command line:
> sn –i codeSigningKey.pfx VS_KEY_EFCA4C5B6DFD4B4F
sn –i .\codeSigningKey.pfx VS_KEY_62CA73019ED23333
The container name may be different.
# Check if the assembly has a strong name
> sn -vf MQTTnet.dll
sn -vf MQTTnet.dll
+4 -2
View File
@@ -53,6 +53,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Server", "Source\MQ
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{2BD01D53-4CA5-4142-BE8D-313876395E3E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Apps", "Apps", "{A56E3128-1639-4F31-873A-325E14BB6295}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -270,7 +272,7 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{FF1F72D6-9524-4422-9497-3CC0002216ED} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{FF1F72D6-9524-4422-9497-3CC0002216ED} = {A56E3128-1639-4F31-873A-325E14BB6295}
{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}
@@ -279,7 +281,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}
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B} = {A56E3128-1639-4F31-873A-325E14BB6295}
{2BD01D53-4CA5-4142-BE8D-313876395E3E} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
+1 -4
View File
@@ -7,8 +7,7 @@
[![NuGet Badge](https://buildstats.info/nuget/MQTTnet)](https://www.nuget.org/packages/MQTTnet)
[![Build status](https://ci.appveyor.com/api/projects/status/ycit86voxfevm2aa/branch/master?svg=true)](https://ci.appveyor.com/project/chkr1011/mqttnet)
[![BCH compliance](https://bettercodehub.com/edge/badge/chkr1011/MQTTnet?branch=master)](https://bettercodehub.com/)
[![OpenCollective](https://opencollective.com/mqttnet/backers/badge.svg)](https://opencollective.com/mqttnet)
[![OpenCollective](https://opencollective.com/mqttnet/sponsors/badge.svg)](https://opencollective.com/mqttnet) [![Join the chat at https://gitter.im/MQTTnet/community](https://badges.gitter.im/MQTTnet/community.svg)](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Join the chat at https://gitter.im/MQTTnet/community](https://badges.gitter.im/MQTTnet/community.svg)](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://raw.githubusercontent.com/chkr1011/MQTTnet/master/LICENSE)
# MQTTnet
@@ -93,8 +92,6 @@ 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, like all projects requires time and money. If you feel compelled to support the development team, you can do so through Open Collective (https://opencollective.com/mqttnet)**.
## References
This library is used in the following projects:
@@ -1,17 +1,16 @@
using System;
using System.Net;
using MQTTnet.Adapter;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using System;
using System.Net;
namespace MQTTnet.AspNetCore.Client
{
public class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
{
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
@@ -1,13 +1,13 @@
using System;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
using MQTTnet.Exceptions;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
using MQTTnet.Exceptions;
namespace MQTTnet.AspNetCore.Client.Tcp
{
@@ -40,8 +40,11 @@ namespace MQTTnet.AspNetCore.Client.Tcp
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
}
public Task DisposeAsync()
#if NETCOREAPP3_1
public override ValueTask DisposeAsync()
#else
public new Task DisposeAsync()
#endif
{
IsConnected = false;
@@ -50,8 +53,15 @@ namespace MQTTnet.AspNetCore.Client.Tcp
_socket?.Dispose();
#if NETCOREAPP3_1
return base.DisposeAsync();
}
#else
return Task.CompletedTask;
}
#endif
public async Task StartAsync()
{
@@ -1,14 +1,13 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using MQTTnet.Server;
using System.Collections.Generic;
namespace MQTTnet.AspNetCore
{
public static class ApplicationBuilderExtensions
{
[Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
{
app.UseWebSockets();
@@ -24,7 +23,7 @@ namespace MQTTnet.AspNetCore
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
{
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
subProtocol = MqttSubProtocolSelector.SelectSubProtocol(requestedSubProtocolValues);
}
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
@@ -37,14 +36,6 @@ namespace MQTTnet.AspNetCore
return app;
}
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
{
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
return requestedSubProtocolValues
.OrderByDescending(p => p.Length)
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
}
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<IMqttServer> configure)
{
var server = app.ApplicationServices.GetRequiredService<IMqttServer>();
@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Connections;
using System;
namespace MQTTnet.AspNetCore
{
public static class ConnectionRouteBuilderExtensions
{
#if NETCOREAPP3_1
[Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
#endif
public static void MapMqtt(this ConnectionsRouteBuilder connection, PathString path)
{
connection.MapConnectionHandler<MqttConnectionHandler>(path, options =>
{
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
});
}
}
}
@@ -0,0 +1,21 @@
#if NETCOREAPP3_1
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
namespace MQTTnet.AspNetCore
{
public static class EndpointRouterExtensions
{
public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
{
endpoints.MapConnectionHandler<MqttConnectionHandler>(pattern, options =>
{
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
});
}
}
}
#endif
@@ -80,7 +80,7 @@ namespace MQTTnet.AspNetCore
headerLength = 0;
bodyLength = 0;
var temp = input.Slice(0, Math.Min(5, input.Length)).GetMemory();
var temp = input.Slice(0, Math.Min(5, input.Length)).GetMemory().Span;
do
{
@@ -88,7 +88,7 @@ namespace MQTTnet.AspNetCore
{
return false;
}
encodedByte = temp.Span[index];
encodedByte = temp[index];
index++;
value += (byte)(encodedByte & 127) * multiplier;
@@ -1,10 +1,10 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Server;
using MQTTnet.Implementations;
using MQTTnet.Server;
using System;
namespace MQTTnet.AspNetCore
{
@@ -13,7 +13,7 @@ namespace MQTTnet.AspNetCore
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, IMqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
services.AddSingleton(options);
services.AddHostedMqttServer();
@@ -23,7 +23,8 @@ namespace MQTTnet.AspNetCore
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure)
{
services.AddSingleton<IMqttServerOptions>(s => {
services.AddSingleton<IMqttServerOptions>(s =>
{
var builder = new MqttServerOptionsBuilder();
configure(builder);
return builder.Build();
@@ -36,7 +37,8 @@ namespace MQTTnet.AspNetCore
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
{
services.AddSingleton<IMqttServerOptions>(s => {
services.AddSingleton<IMqttServerOptions>(s =>
{
var builder = new AspNetMqttServerOptionsBuilder(s);
configure(builder);
return builder.Build();
@@ -60,14 +62,12 @@ namespace MQTTnet.AspNetCore
private static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
{
var logger = new MqttNetLogger();
var childLogger = logger.CreateChildLogger();
services.AddSingleton<IMqttNetLogger>(logger);
services.AddSingleton(childLogger);
services.AddSingleton<MqttHostedServer>();
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
services.AddSingleton<IMqttServer>(s => s.GetService<MqttHostedServer>());
return services;
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp2.1</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
<Product />
<Company />
<Authors />
@@ -14,11 +14,17 @@
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.0.3" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.1" />
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="System.IO.Pipelines" Version="4.7.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1'">
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="3.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.3" />
</ItemGroup>
<ItemGroup>
@@ -21,7 +21,8 @@ namespace MQTTnet.AspNetCore
public Task StartAsync(CancellationToken cancellationToken)
{
return StartAsync(_options);
_ = StartAsync(_options);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
@@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Linq;
namespace MQTTnet.AspNetCore
{
public static class MqttSubProtocolSelector
{
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
{
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
return requestedSubProtocolValues
.OrderByDescending(p => p.Length)
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
}
}
}
@@ -1,20 +1,20 @@
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Server;
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
namespace MQTTnet.AspNetCore
{
public class MqttWebSocketServerAdapter : IMqttServerAdapter
{
private readonly IMqttNetChildLogger _logger;
private readonly IMqttNetLogger _logger;
public MqttWebSocketServerAdapter(IMqttNetChildLogger logger)
public MqttWebSocketServerAdapter(IMqttNetLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
@@ -38,7 +38,7 @@ namespace MQTTnet.AspNetCore
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
try
{
@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Publishing;
@@ -13,18 +8,33 @@ using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Extensions.ManagedClient
{
public class ManagedMqttClient : IManagedMqttClient
public class ManagedMqttClient : Disposable, IManagedMqttClient
{
private readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>();
/// <summary>
/// The subscriptions are managed in 2 separate buckets:
/// <see cref="_subscriptions"/> and <see cref="_unsubscriptions"/> are processed during normal operation
/// and are moved to the <see cref="_reconnectSubscriptions"/> when they get processed. They can be accessed by
/// any thread and are therefore mutex'ed. <see cref="_reconnectSubscriptions"/> get sent to the broker
/// at reconnect and are solely owned by <see cref="MaintainConnectionAsync"/>.
/// </summary>
private readonly Dictionary<string, MqttQualityOfServiceLevel> _reconnectSubscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
private readonly HashSet<string> _unsubscriptions = new HashSet<string>();
private readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
private readonly IMqttClient _mqttClient;
private readonly IMqttNetChildLogger _logger;
private readonly IMqttNetLogger _logger;
private readonly AsyncLock _messageQueueLock = new AsyncLock();
private CancellationTokenSource _connectionCancellationToken;
@@ -33,10 +43,7 @@ namespace MQTTnet.Extensions.ManagedClient
private ManagedMqttClientStorageManager _storageManager;
private bool _disposed;
private bool _subscriptionsNotPushed;
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetChildLogger logger)
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
{
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
@@ -82,10 +89,6 @@ namespace MQTTnet.Extensions.ManagedClient
if (options == null) throw new ArgumentNullException(nameof(options));
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
if (!options.ClientOptions.CleanSession)
{
throw new NotSupportedException("The managed client does not support existing sessions.");
}
if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
@@ -141,6 +144,7 @@ namespace MQTTnet.Extensions.ManagedClient
ThrowIfDisposed();
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
if (Options == null) throw new InvalidOperationException("call StartAsync before publishing messages");
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage.Topic);
@@ -169,7 +173,7 @@ namespace MQTTnet.Extensions.ManagedClient
}
_messageQueue.Enqueue(applicationMessage);
if (_storageManager != null)
{
if (removedMessage != null)
@@ -206,9 +210,10 @@ namespace MQTTnet.Extensions.ManagedClient
foreach (var topicFilter in topicFilters)
{
_subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
_subscriptionsNotPushed = true;
_unsubscriptions.Remove(topicFilter.Topic);
}
}
_subscriptionsQueuedSignal.Release();
return Task.FromResult(0);
}
@@ -223,45 +228,34 @@ namespace MQTTnet.Extensions.ManagedClient
{
foreach (var topic in topics)
{
if (_subscriptions.Remove(topic))
{
_unsubscriptions.Add(topic);
_subscriptionsNotPushed = true;
}
_subscriptions.Remove(topic);
_unsubscriptions.Add(topic);
}
}
_subscriptionsQueuedSignal.Release();
return Task.FromResult(0);
}
public void Dispose()
protected override void Dispose(bool disposing)
{
if (_disposed)
if (disposing)
{
return;
}
_disposed = true;
StopPublishing();
StopMaintainingConnection();
if (_maintainConnectionTask != null)
{
Task.WaitAny(_maintainConnectionTask);
_maintainConnectionTask = null;
}
_messageQueueLock.Dispose();
_mqttClient.Dispose();
}
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(ManagedMqttClient));
StopPublishing();
StopMaintainingConnection();
if (_maintainConnectionTask != null)
{
_maintainConnectionTask.GetAwaiter().GetResult();
_maintainConnectionTask = null;
}
_messageQueue.Dispose();
_messageQueueLock.Dispose();
_mqttClient.Dispose();
_subscriptionsQueuedSignal.Dispose();
}
base.Dispose(disposing);
}
private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
@@ -278,11 +272,11 @@ namespace MQTTnet.Extensions.ManagedClient
}
catch (Exception exception)
{
_logger.Error(exception, "Unhandled exception while maintaining connection.");
_logger.Error(exception, "Error exception while maintaining connection.");
}
finally
{
if (!_disposed)
if (!IsDisposed)
{
try
{
@@ -295,6 +289,12 @@ namespace MQTTnet.Extensions.ManagedClient
_logger.Info("Stopped");
}
_reconnectSubscriptions.Clear();
lock (_subscriptions)
{
_subscriptions.Clear();
_unsubscriptions.Clear();
}
}
}
@@ -310,16 +310,22 @@ namespace MQTTnet.Extensions.ManagedClient
return;
}
if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed)
if (connectionState == ReconnectionResult.Reconnected)
{
await PublishReconnectSubscriptionsAsync().ConfigureAwait(false);
StartPublishing();
return;
}
if (connectionState == ReconnectionResult.Recovered)
{
await SynchronizeSubscriptionsAsync().ConfigureAwait(false);
StartPublishing();
return;
}
if (connectionState == ReconnectionResult.StillConnected)
{
await Task.Delay(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
await PublishSubscriptionsAsync(Options.ConnectionCheckInterval, cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
@@ -327,11 +333,11 @@ namespace MQTTnet.Extensions.ManagedClient
}
catch (MqttCommunicationException exception)
{
_logger.Warning(exception, "Communication exception while maintaining connection.");
_logger.Warning(exception, "Communication error while maintaining connection.");
}
catch (Exception exception)
{
_logger.Error(exception, "Unhandled exception while maintaining connection.");
_logger.Error(exception, "Error exception while maintaining connection.");
}
}
@@ -349,7 +355,7 @@ namespace MQTTnet.Extensions.ManagedClient
// of the messages, the DropOldestQueuedMessage strategy would
// be unable to know which message is actually the oldest and would
// instead drop the first item in the queue.
var message = _messageQueue.PeekAndWait();
var message = _messageQueue.PeekAndWait(cancellationToken);
if (message == null)
{
continue;
@@ -389,7 +395,7 @@ namespace MQTTnet.Extensions.ManagedClient
// it from the queue. If not, that means this.PublishAsync has already
// removed it, in which case we don't want to do anything.
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
if (_storageManager != null)
{
await _storageManager.RemoveAsync(message).ConfigureAwait(false);
@@ -414,7 +420,7 @@ namespace MQTTnet.Extensions.ManagedClient
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
{
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
if (_storageManager != null)
{
await _storageManager.RemoveAsync(message).ConfigureAwait(false);
@@ -438,50 +444,84 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
private async Task SynchronizeSubscriptionsAsync()
private async Task PublishSubscriptionsAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
_logger.Info("Synchronizing subscriptions");
List<TopicFilter> subscriptions;
HashSet<string> unsubscriptions;
lock (_subscriptions)
var endTime = DateTime.UtcNow + timeout;
while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false))
{
subscriptions = _subscriptions.Select(i => new TopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList();
List<TopicFilter> subscriptions;
HashSet<string> unsubscriptions;
unsubscriptions = new HashSet<string>(_unsubscriptions);
_unsubscriptions.Clear();
lock (_subscriptions)
{
subscriptions = _subscriptions.Select(i => new TopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }).ToList();
_subscriptions.Clear();
unsubscriptions = new HashSet<string>(_unsubscriptions);
_unsubscriptions.Clear();
}
_subscriptionsNotPushed = false;
if (!subscriptions.Any() && !unsubscriptions.Any())
{
continue;
}
_logger.Verbose($"Publishing subscriptions ({subscriptions.Count} subscriptions and {unsubscriptions.Count} unsubscriptions)");
foreach (var unsubscription in unsubscriptions)
{
_reconnectSubscriptions.Remove(unsubscription);
}
foreach (var subscription in subscriptions)
{
_reconnectSubscriptions[subscription.Topic] = subscription.QualityOfServiceLevel;
}
try
{
if (unsubscriptions.Any())
{
await _mqttClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false);
}
if (subscriptions.Any())
{
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
}
}
catch (Exception exception)
{
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
}
}
}
if (!subscriptions.Any() && !unsubscriptions.Any())
{
return;
}
private async Task PublishReconnectSubscriptionsAsync()
{
_logger.Info("Publishing subscriptions at reconnect");
try
{
if (unsubscriptions.Any())
{
await _mqttClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false);
}
if (subscriptions.Any())
if (_reconnectSubscriptions.Any())
{
var subscriptions = _reconnectSubscriptions.Select(i => new TopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value });
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
}
}
catch (Exception exception)
{
_logger.Warning(exception, "Synchronizing subscriptions failed.");
_subscriptionsNotPushed = true;
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
}
}
var synchronizingSubscriptionsFailedHandler = SynchronizingSubscriptionsFailedHandler;
if (SynchronizingSubscriptionsFailedHandler != null)
{
await synchronizingSubscriptionsFailedHandler.HandleSynchronizingSubscriptionsFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
}
private async Task HandleSubscriptionExceptionAsync(Exception exception)
{
_logger.Warning(exception, "Synchronizing subscriptions failed.");
var synchronizingSubscriptionsFailedHandler = SynchronizingSubscriptionsFailedHandler;
if (SynchronizingSubscriptionsFailedHandler != null)
{
await synchronizingSubscriptionsFailedHandler.HandleSynchronizingSubscriptionsFailedAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
}
}
@@ -494,8 +534,8 @@ namespace MQTTnet.Extensions.ManagedClient
try
{
await _mqttClient.ConnectAsync(Options.ClientOptions).ConfigureAwait(false);
return ReconnectionResult.Reconnected;
var result = await _mqttClient.ConnectAsync(Options.ClientOptions).ConfigureAwait(false);
return result.IsSessionPresent ? ReconnectionResult.Recovered : ReconnectionResult.Reconnected;
}
catch (Exception exception)
{
@@ -508,7 +548,7 @@ namespace MQTTnet.Extensions.ManagedClient
return ReconnectionResult.NotConnected;
}
}
private void StartPublishing()
{
if (_publishingCancellationToken != null)
@@ -535,5 +575,11 @@ namespace MQTTnet.Extensions.ManagedClient
_connectionCancellationToken?.Dispose();
_connectionCancellationToken = null;
}
private TimeSpan GetRemainingTime(DateTime endTime)
{
var remainingTime = endTime - DateTime.UtcNow;
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
}
}
}
@@ -1,5 +1,5 @@
using System;
using MQTTnet.Diagnostics;
using MQTTnet.Diagnostics;
using System;
namespace MQTTnet.Extensions.ManagedClient
{
@@ -9,7 +9,7 @@ namespace MQTTnet.Extensions.ManagedClient
{
if (factory == null) throw new ArgumentNullException(nameof(factory));
return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger.CreateChildLogger());
return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
}
public static IManagedMqttClient CreateManagedMqttClient(this IMqttFactory factory, IMqttNetLogger logger)
@@ -17,7 +17,7 @@ namespace MQTTnet.Extensions.ManagedClient
if (factory == null) throw new ArgumentNullException(nameof(factory));
if (logger == null) throw new ArgumentNullException(nameof(logger));
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger.CreateChildLogger());
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger);
}
}
}
@@ -4,6 +4,7 @@
{
StillConnected,
Reconnected,
Recovered,
NotConnected
}
}
@@ -1,17 +1,17 @@
using System;
using System.Collections.Concurrent;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client;
using MQTTnet.Client;
using MQTTnet.Exceptions;
using MQTTnet.Extensions.Rpc.Options;
using MQTTnet.Extensions.Rpc.Options.TopicGeneration;
using MQTTnet.Protocol;
using System;
using System.Collections.Concurrent;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Extensions.Rpc
{
public class MqttRpcClient : IDisposable
public sealed class MqttRpcClient : IDisposable
{
private readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
private readonly IMqttClient _mqttClient;
@@ -101,7 +101,7 @@ namespace MQTTnet.Extensions.Rpc
await _mqttClient.SubscribeAsync(responseTopic, qualityOfServiceLevel).ConfigureAwait(false);
await _mqttClient.PublishAsync(requestMessage).ConfigureAwait(false);
using (var timeoutCts = new CancellationTokenSource(timeout))
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token))
{
@@ -85,7 +85,12 @@ namespace MQTTnet.Extensions.WebSocket4Net
{
foreach (var certificate in _webSocketOptions.TlsOptions.Certificates)
{
#if WINDOWS_UWP
certificates.Add(new X509Certificate(certificate));
#else
certificates.Add(certificate);
#endif
}
}
@@ -1,15 +1,15 @@
using System;
using MQTTnet.Adapter;
using MQTTnet.Adapter;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using System;
namespace MQTTnet.Extensions.WebSocket4Net
{
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
{
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (logger == null) throw new ArgumentNullException(nameof(logger));
@@ -17,19 +17,19 @@ namespace MQTTnet.Extensions.WebSocket4Net
switch (options.ChannelOptions)
{
case MqttClientTcpOptions _:
{
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion), logger);
}
{
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion), logger);
}
case MqttClientWebSocketOptions webSocketOptions:
{
return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options, webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion), logger);
}
{
return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options, webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion), logger);
}
default:
{
throw new NotSupportedException();
}
{
throw new NotSupportedException();
}
}
}
}
@@ -1,43 +1,43 @@
using System;
using MQTTnet.Diagnostics;
//using MQTTnet.Diagnostics;
//using System;
namespace MQTTnet.Server.Logging
{
public class MqttNetChildLoggerWrapper : IMqttNetChildLogger
{
private readonly MqttNetLoggerWrapper _logger;
private readonly string _source;
//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));
// public MqttNetChildLoggerWrapper(string source, MqttNetLoggerWrapper logger)
// {
// _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_source = source;
}
// _source = source;
// }
public IMqttNetChildLogger CreateChildLogger(string source = null)
{
return _logger.CreateChildLogger(source);
}
// public IMqttNetLogger CreateChildLogger(string source = null)
// {
// return _logger.CreateChildLogger(source);
// }
public void Verbose(string message, params object[] parameters)
{
_logger.Publish(MqttNetLogLevel.Verbose, _source, message, parameters, null);
}
// public void 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 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 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);
}
}
}
// public void Error(Exception exception, string message, params object[] parameters)
// {
// _logger.Publish(MqttNetLogLevel.Error, _source, message, parameters, exception);
// }
// }
//}
@@ -1,13 +1,13 @@
using System;
using System.Threading;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using MQTTnet.Diagnostics;
using System;
using System.Threading;
namespace MQTTnet.Server.Logging
{
public class MqttNetLoggerWrapper : IMqttNetLogger
{
private readonly ILogger<MqttServer> _logger;
readonly ILogger<MqttServer> _logger;
public MqttNetLoggerWrapper(ILogger<MqttServer> logger)
{
@@ -16,25 +16,39 @@ namespace MQTTnet.Server.Logging
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
public IMqttNetChildLogger CreateChildLogger(string source = null)
public IMqttNetLogger CreateChildLogger(string source)
{
return new MqttNetChildLoggerWrapper(source, this);
return new MqttNetLogger(source);
}
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
public void Publish(MqttNetLogLevel level, string source, string message, object[] parameters, Exception exception)
{
var convertedLogLevel = ConvertLogLevel(logLevel);
var convertedLogLevel = ConvertLogLevel(level);
_logger.Log(convertedLogLevel, exception, message, parameters);
var logMessagePublishedEvent = LogMessagePublished;
if (logMessagePublishedEvent != null)
{
var logMessage = new MqttNetLogMessage(null, DateTime.UtcNow, Thread.CurrentThread.ManagedThreadId, source, logLevel, message, exception);
var logMessage = new MqttNetLogMessage
{
Timestamp = DateTime.UtcNow,
ThreadId = Thread.CurrentThread.ManagedThreadId,
Source = source,
Level = level,
Message = message,
Exception = exception
};
logMessagePublishedEvent.Invoke(this, new MqttNetLogMessagePublishedEventArgs(logMessage));
}
}
private static LogLevel ConvertLogLevel(MqttNetLogLevel logLevel)
public void Publish(MqttNetLogLevel logLevel, string message, object[] parameters, Exception exception)
{
Publish(logLevel, null, message, parameters, exception);
}
static LogLevel ConvertLogLevel(MqttNetLogLevel logLevel)
{
switch (logLevel)
{
+7 -9
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<AssemblyName>MQTTnet.Server</AssemblyName>
<RootNamespace>MQTTnet.Server</RootNamespace>
@@ -43,14 +43,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="IronPython" Version="2.7.9" />
<PackageReference Include="IronPython.StdLib" Version="2.7.9" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.0.0-rc2" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.0.0-rc2" />
<PackageReference Include="IronPython" Version="2.7.10-candidate1" />
<PackageReference Include="IronPython.StdLib" Version="2.7.10-candidate1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.3.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.3.1" />
</ItemGroup>
<ItemGroup>
+31 -27
View File
@@ -1,11 +1,4 @@
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 IronPython.Runtime;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using MQTTnet.Adapter;
@@ -16,25 +9,33 @@ using MQTTnet.Protocol;
using MQTTnet.Server.Configuration;
using MQTTnet.Server.Scripting;
using MQTTnet.Server.Status;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
namespace MQTTnet.Server.Mqtt
{
public class MqttServerService
{
private readonly ILogger<MqttServerService> _logger;
readonly ILogger<MqttServerService> _logger;
private readonly MqttSettingsModel _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 MqttServerConnectionValidator _mqttConnectionValidator;
private readonly IMqttServer _mqttServer;
private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly MqttWebSocketServerAdapter _webSocketServerAdapter;
readonly MqttSettingsModel _settings;
readonly MqttApplicationMessageInterceptor _mqttApplicationMessageInterceptor;
readonly MqttServerStorage _mqttServerStorage;
readonly MqttClientConnectedHandler _mqttClientConnectedHandler;
readonly MqttClientDisconnectedHandler _mqttClientDisconnectedHandler;
readonly MqttClientSubscribedTopicHandler _mqttClientSubscribedTopicHandler;
readonly MqttClientUnsubscribedTopicHandler _mqttClientUnsubscribedTopicHandler;
readonly MqttServerConnectionValidator _mqttConnectionValidator;
readonly IMqttServer _mqttServer;
readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor;
readonly MqttUnsubscriptionInterceptor _mqttUnsubscriptionInterceptor;
readonly PythonScriptHostService _pythonScriptHostService;
readonly MqttWebSocketServerAdapter _webSocketServerAdapter;
public MqttServerService(
MqttSettingsModel mqttSettings,
@@ -45,6 +46,7 @@ namespace MQTTnet.Server.Mqtt
MqttClientUnsubscribedTopicHandler mqttClientUnsubscribedTopicHandler,
MqttServerConnectionValidator mqttConnectionValidator,
MqttSubscriptionInterceptor mqttSubscriptionInterceptor,
MqttUnsubscriptionInterceptor mqttUnsubscriptionInterceptor,
MqttApplicationMessageInterceptor mqttApplicationMessageInterceptor,
MqttServerStorage mqttServerStorage,
PythonScriptHostService pythonScriptHostService,
@@ -57,16 +59,17 @@ namespace MQTTnet.Server.Mqtt
_mqttClientUnsubscribedTopicHandler = mqttClientUnsubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientUnsubscribedTopicHandler));
_mqttConnectionValidator = mqttConnectionValidator ?? throw new ArgumentNullException(nameof(mqttConnectionValidator));
_mqttSubscriptionInterceptor = mqttSubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttSubscriptionInterceptor));
_mqttUnsubscriptionInterceptor = mqttUnsubscriptionInterceptor ?? throw new ArgumentNullException(nameof(mqttUnsubscriptionInterceptor));
_mqttApplicationMessageInterceptor = mqttApplicationMessageInterceptor ?? throw new ArgumentNullException(nameof(mqttApplicationMessageInterceptor));
_mqttServerStorage = mqttServerStorage ?? throw new ArgumentNullException(nameof(mqttServerStorage));
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_webSocketServerAdapter = new MqttWebSocketServerAdapter(mqttFactory.Logger.CreateChildLogger());
_webSocketServerAdapter = new MqttWebSocketServerAdapter(mqttFactory.Logger);
var adapters = new List<IMqttServerAdapter>
{
new MqttTcpServerAdapter(mqttFactory.Logger.CreateChildLogger())
new MqttTcpServerAdapter(mqttFactory.Logger)
{
TreatSocketOpeningErrorAsWarning = true // Opening other ports than for HTTP is not allows in Azure App Services.
},
@@ -124,7 +127,7 @@ namespace MQTTnet.Server.Mqtt
return _mqttServer.PublishAsync(applicationMessage);
}
private void Publish(PythonDictionary parameters)
void Publish(PythonDictionary parameters)
{
try
{
@@ -170,7 +173,7 @@ namespace MQTTnet.Server.Mqtt
}
}
private IMqttServerOptions CreateMqttServerOptions()
IMqttServerOptions CreateMqttServerOptions()
{
var options = new MqttServerOptionsBuilder()
.WithMaxPendingMessagesPerClient(_settings.MaxPendingMessagesPerClient)
@@ -178,6 +181,7 @@ namespace MQTTnet.Server.Mqtt
.WithConnectionValidator(_mqttConnectionValidator)
.WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor)
.WithSubscriptionInterceptor(_mqttSubscriptionInterceptor)
.WithUnsubscriptionInterceptor(_mqttUnsubscriptionInterceptor)
.WithStorage(_mqttServerStorage);
// Configure unencrypted connections
@@ -211,7 +215,7 @@ namespace MQTTnet.Server.Mqtt
options
.WithEncryptedEndpoint()
.WithEncryptionSslProtocol(SslProtocols.Tls12);
if (!string.IsNullOrEmpty(_settings.EncryptedTcpEndPoint?.Certificate?.Path))
{
IMqttServerCertificateCredentials certificateCredentials = null;
@@ -226,7 +230,7 @@ namespace MQTTnet.Server.Mqtt
options.WithEncryptionCertificate(_settings.EncryptedTcpEndPoint.Certificate.ReadCertificate(), certificateCredentials);
}
if (_settings.EncryptedTcpEndPoint.TryReadIPv4(out var address4))
{
options.WithEncryptedEndpointBoundIPAddress(address4);
@@ -0,0 +1,48 @@
using System;
using System.Threading.Tasks;
using IronPython.Runtime;
using Microsoft.Extensions.Logging;
using MQTTnet.Server.Scripting;
namespace MQTTnet.Server.Mqtt
{
public class MqttUnsubscriptionInterceptor : IMqttServerUnsubscriptionInterceptor
{
private readonly PythonScriptHostService _pythonScriptHostService;
private readonly ILogger _logger;
public MqttUnsubscriptionInterceptor(PythonScriptHostService pythonScriptHostService, ILogger<MqttUnsubscriptionInterceptor> logger)
{
_pythonScriptHostService = pythonScriptHostService ?? throw new ArgumentNullException(nameof(pythonScriptHostService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task InterceptUnsubscriptionAsync(MqttUnsubscriptionInterceptorContext context)
{
try
{
var sessionItems = (PythonDictionary)context.SessionItems[MqttServerConnectionValidator.WrappedSessionItemsKey];
var pythonContext = new PythonDictionary
{
{ "client_id", context.ClientId },
{ "session_items", sessionItems },
{ "topic", context.Topic },
{ "accept_unsubscription", context.AcceptUnsubscription },
{ "close_connection", context.CloseConnection }
};
_pythonScriptHostService.InvokeOptionalFunction("on_intercept_unsubscription", pythonContext);
context.AcceptUnsubscription = (bool)pythonContext["accept_unsubscription"];
context.CloseConnection = (bool)pythonContext["close_connection"];
}
catch (Exception exception)
{
_logger.LogError(exception, "Error while intercepting unsubscription.");
}
return Task.CompletedTask;
}
}
}
+15 -7
View File
@@ -1,8 +1,8 @@
using System;
using System.Reflection;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using MQTTnet.Server.Web;
using System;
using System.Reflection;
namespace MQTTnet.Server
{
@@ -14,7 +14,15 @@ namespace MQTTnet.Server
{
PrintLogo();
WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build().Run();
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(serverOptions =>
{
})
.UseStartup<Startup>();
}).Build().Run();
return 0;
}
@@ -25,7 +33,7 @@ namespace MQTTnet.Server
}
}
private static void PrintLogo()
static void PrintLogo()
{
Console.ResetColor();
Console.ForegroundColor = ConsoleColor.Red;
@@ -46,7 +54,7 @@ namespace MQTTnet.Server
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("The official MQTT server implementation of MQTTnet");
Console.WriteLine("Copyright (c) 2017-2019 The MQTTnet Team");
Console.WriteLine("Copyright (c) 2017-2020 The MQTTnet Team");
Console.WriteLine(@"https://github.com/chkr1011/MQTTnet");
Console.ForegroundColor = ConsoleColor.White;
@@ -1,14 +1,14 @@
using System;
using Microsoft.Extensions.Logging;
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;
readonly Dictionary<string, object> _storage = new Dictionary<string, object>();
readonly PythonScriptHostService _pythonScriptHostService;
readonly ILogger<DataSharingService> _logger;
public DataSharingService(PythonScriptHostService pythonScriptHostService, ILogger<DataSharingService> logger)
{
@@ -1,14 +1,14 @@
using System;
using Microsoft.Extensions.Logging;
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;
readonly ILogger _logger;
readonly Encoding _encoder = Encoding.UTF8;
public PythonIOStream(ILogger<PythonIOStream> logger)
{
@@ -1,4 +1,8 @@
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using MQTTnet.Server.Configuration;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
@@ -6,22 +10,18 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using MQTTnet.Server.Configuration;
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 string _scriptsPath;
private readonly ScriptingSettingsModel _scriptingSettings;
private readonly ILogger<PythonScriptHostService> _logger;
private readonly ScriptEngine _scriptEngine;
readonly IDictionary<string, object> _proxyObjects = new ExpandoObject();
readonly List<PythonScriptInstance> _scriptInstances = new List<PythonScriptInstance>();
readonly string _scriptsPath;
readonly ScriptingSettingsModel _scriptingSettings;
readonly ILogger<PythonScriptHostService> _logger;
readonly ScriptEngine _scriptEngine;
public PythonScriptHostService(ScriptingSettingsModel scriptingSettings, PythonIOStream pythonIOStream, ILogger<PythonScriptHostService> logger)
{
_scriptingSettings = scriptingSettings ?? throw new ArgumentNullException(nameof(scriptingSettings));
@@ -139,7 +139,7 @@ namespace MQTTnet.Server.Scripting
}
}
private async Task TryInitializeScriptAsync(string path)
async Task TryInitializeScriptAsync(string path)
{
var uid = new FileInfo(path).Name.Replace(".py", string.Empty, StringComparison.OrdinalIgnoreCase);
@@ -155,7 +155,7 @@ namespace MQTTnet.Server.Scripting
{
_scriptInstances.Add(scriptInstance);
}
_logger.LogInformation($"Initialized script '{uid}'.");
}
catch (Exception exception)
@@ -164,7 +164,7 @@ namespace MQTTnet.Server.Scripting
}
}
private PythonScriptInstance CreateScriptInstance(string uid, string path, string code)
PythonScriptInstance CreateScriptInstance(string uid, string path, string code)
{
var scriptScope = _scriptEngine.CreateScope();
@@ -173,11 +173,11 @@ namespace MQTTnet.Server.Scripting
scriptScope.SetVariable("mqtt_net_server", _proxyObjects);
compiledCode.Execute(scriptScope);
return new PythonScriptInstance(uid, path, scriptScope);
}
private void AddSearchPaths(ScriptEngine scriptEngine)
void AddSearchPaths(ScriptEngine scriptEngine)
{
if (_scriptingSettings.IncludePaths?.Any() != true)
{
@@ -1,12 +1,12 @@
using System;
using IronPython.Runtime;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
using System;
namespace MQTTnet.Server.Scripting
{
public class PythonScriptInstance
{
private readonly ScriptScope _scriptScope;
readonly ScriptScope _scriptScope;
public PythonScriptInstance(string uid, string path, ScriptScope scriptScope)
{
+23 -27
View File
@@ -1,10 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -16,7 +11,12 @@ using MQTTnet.Server.Logging;
using MQTTnet.Server.Mqtt;
using MQTTnet.Server.Scripting;
using MQTTnet.Server.Scripting.DataSharing;
using Newtonsoft.Json.Converters;
using Swashbuckle.AspNetCore.SwaggerUI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace MQTTnet.Server.Web
{
@@ -35,33 +35,27 @@ namespace MQTTnet.Server.Web
public void Configure(
IApplicationBuilder application,
IHostingEnvironment environment,
MqttServerService mqttServerService,
PythonScriptHostService pythonScriptHostService,
DataSharingService dataSharingService,
MqttSettingsModel mqttSettings)
{
if (environment.IsDevelopment())
{
application.UseDeveloperExceptionPage();
}
else
{
application.UseHsts();
}
application.UseHsts();
application.UseRouting();
application.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
.AllowAnyHeader());
application.UseAuthentication();
application.UseAuthorization();
application.UseStaticFiles();
application.UseHttpsRedirection();
application.UseMvc();
application.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
ConfigureWebSocketEndpoint(application, mqttServerService, mqttSettings);
@@ -87,11 +81,14 @@ namespace MQTTnet.Server.Web
{
services.AddCors();
services.AddControllers();
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(options =>
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.AddNewtonsoftJson(o =>
{
options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
o.SerializerSettings.Converters.Add(new StringEnumConverter());
});
ReadMqttSettings(services);
@@ -111,12 +108,11 @@ namespace MQTTnet.Server.Web
services.AddSingleton<MqttClientUnsubscribedTopicHandler>();
services.AddSingleton<MqttServerConnectionValidator>();
services.AddSingleton<MqttSubscriptionInterceptor>();
services.AddSingleton<MqttUnsubscriptionInterceptor>();
services.AddSingleton<MqttApplicationMessageInterceptor>();
services.AddSwaggerGen(c =>
{
c.DescribeAllEnumsAsStrings();
var securityScheme = new OpenApiSecurityScheme
{
Scheme = "Basic",
@@ -156,7 +152,7 @@ namespace MQTTnet.Server.Web
.AddCookie();
}
private void ReadMqttSettings(IServiceCollection services)
void ReadMqttSettings(IServiceCollection services)
{
var mqttSettings = new MqttSettingsModel();
Configuration.Bind("MQTT", mqttSettings);
@@ -167,7 +163,7 @@ namespace MQTTnet.Server.Web
services.AddSingleton(scriptingSettings);
}
private static void ConfigureWebSocketEndpoint(
static void ConfigureWebSocketEndpoint(
IApplicationBuilder application,
MqttServerService mqttServerService,
MqttSettingsModel mqttSettings)
+1 -5
View File
@@ -52,11 +52,7 @@
},
"Scripting": {
"ScriptsPath": "Scripts",
"IncludePaths": [
"Lib",
"/usr/lib/python2.7",
"C:\\Python27\\Lib"
]
"IncludePaths": []
},
"Logging": {
"LogLevel": {
-12
View File
@@ -1,12 +0,0 @@
{
"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"
}
}
@@ -5,6 +5,6 @@ namespace MQTTnet.Adapter
{
public interface IMqttClientAdapterFactory
{
IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger);
IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetLogger logger);
}
}
+35 -39
View File
@@ -1,3 +1,9 @@
using MQTTnet.Channel;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Packets;
using System;
using System.IO;
using System.Net.Sockets;
@@ -5,34 +11,26 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Packets;
namespace MQTTnet.Adapter
{
public class MqttChannelAdapter : IMqttChannelAdapter
public sealed class MqttChannelAdapter : Disposable, IMqttChannelAdapter
{
private const uint ErrorOperationAborted = 0x800703E3;
private const int ReadBufferSize = 4096; // TODO: Move buffer size to config
const uint ErrorOperationAborted = 0x800703E3;
const int ReadBufferSize = 4096; // TODO: Move buffer size to config
private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
readonly IMqttNetLogger _logger;
readonly IMqttChannel _channel;
readonly MqttPacketReader _packetReader;
private readonly IMqttNetChildLogger _logger;
private readonly IMqttChannel _channel;
private readonly MqttPacketReader _packetReader;
readonly byte[] _fixedHeaderBuffer = new byte[2];
private readonly byte[] _fixedHeaderBuffer = new byte[2];
SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
private bool _isDisposed;
long _bytesReceived;
long _bytesSent;
private long _bytesReceived;
private long _bytesSent;
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetChildLogger logger)
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
@@ -57,7 +55,7 @@ namespace MQTTnet.Adapter
public Action ReadingPacketStartedCallback { get; set; }
public Action ReadingPacketCompletedCallback { get; set; }
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
ThrowIfDisposed();
@@ -145,7 +143,7 @@ namespace MQTTnet.Adapter
}
finally
{
_writerSemaphore.Release();
_writerSemaphore?.Release();
}
}
@@ -209,7 +207,20 @@ namespace MQTTnet.Adapter
Interlocked.Exchange(ref _bytesSent, 0L);
}
private async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
protected override void Dispose(bool disposing)
{
if (disposing)
{
_channel?.Dispose();
_writerSemaphore?.Dispose();
_writerSemaphore = null;
}
base.Dispose(disposing);
}
async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
{
var readFixedHeaderResult = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false);
@@ -269,29 +280,14 @@ namespace MQTTnet.Adapter
}
}
public void Dispose()
{
_isDisposed = true;
_channel?.Dispose();
}
private void ThrowIfDisposed()
{
if (_isDisposed)
{
throw new ObjectDisposedException(nameof(MqttChannelAdapter));
}
}
private static bool IsWrappedException(Exception exception)
static bool IsWrappedException(Exception exception)
{
return exception is OperationCanceledException ||
exception is MqttCommunicationTimedOutException ||
exception is MqttCommunicationException;
}
private static void WrapException(Exception exception)
static void WrapException(Exception exception)
{
if (exception is IOException && exception.InnerException is SocketException innerException)
{
@@ -5,12 +5,13 @@ namespace MQTTnet.Adapter
{
public class MqttConnectingFailedException : MqttCommunicationException
{
public MqttConnectingFailedException(MqttClientConnectResultCode resultCode)
: base($"Connecting with MQTT server failed ({resultCode.ToString()}).")
public MqttConnectingFailedException(MqttClientAuthenticateResult result)
: base($"Connecting with MQTT server failed ({result.ResultCode.ToString()}).")
{
ResultCode = resultCode;
Result = result;
}
public MqttClientConnectResultCode ResultCode { get; }
public MqttClientAuthenticateResult Result { get; }
public MqttClientConnectResultCode ResultCode => Result.ResultCode;
}
}
+198 -98
View File
@@ -1,7 +1,3 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
@@ -17,26 +13,34 @@ using MQTTnet.Internal;
using MQTTnet.PacketDispatcher;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Client
{
public class MqttClient : IMqttClient
public class MqttClient : Disposable, 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();
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
readonly Stopwatch _sendTracker = new Stopwatch();
readonly Stopwatch _receiveTracker = new Stopwatch();
readonly object _disconnectLock = new object();
private readonly IMqttClientAdapterFactory _adapterFactory;
private readonly IMqttNetChildLogger _logger;
readonly IMqttClientAdapterFactory _adapterFactory;
readonly IMqttNetLogger _logger;
private CancellationTokenSource _backgroundCancellationTokenSource;
private Task _packetReceiverTask;
private Task _keepAlivePacketsSenderTask;
CancellationTokenSource _backgroundCancellationTokenSource;
Task _packetReceiverTask;
Task _keepAlivePacketsSenderTask;
Task _publishPacketReceiverTask;
private IMqttChannelAdapter _adapter;
private bool _cleanDisconnectInitiated;
private long _disconnectGate;
AsyncQueue<MqttPublishPacket> _publishPacketReceiverQueue;
IMqttChannelAdapter _adapter;
bool _cleanDisconnectInitiated;
long _disconnectGate;
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
{
@@ -63,6 +67,8 @@ namespace MQTTnet.Client
ThrowIfConnected("It is not allowed to connect with a server after the connection is established.");
ThrowIfDisposed();
MqttClientAuthenticateResult authenticateResult = null;
try
@@ -79,15 +85,22 @@ namespace MQTTnet.Client
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.");
using (var combined = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken))
{
_logger.Verbose($"Trying to connect with server '{options.ChannelOptions}' (Timeout={options.CommunicationTimeout}).");
await _adapter.ConnectAsync(options.CommunicationTimeout, combined.Token).ConfigureAwait(false);
_logger.Verbose("Connection with server established.");
_packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken);
_publishPacketReceiverQueue = new AsyncQueue<MqttPublishPacket>();
_publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken);
authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, cancellationToken).ConfigureAwait(false);
_packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken);
authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false);
}
_sendTracker.Restart();
_receiveTracker.Restart();
if (Options.KeepAlivePeriod != TimeSpan.Zero)
{
@@ -121,6 +134,10 @@ namespace MQTTnet.Client
public async Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken)
{
if (options is null) throw new ArgumentNullException(nameof(options));
ThrowIfDisposed();
try
{
_cleanDisconnectInitiated = true;
@@ -144,12 +161,15 @@ namespace MQTTnet.Client
{
if (data == null) throw new ArgumentNullException(nameof(data));
ThrowIfDisposed();
ThrowIfNotConnected();
return SendAsync(new MqttAuthPacket
{
Properties = new MqttAuthPacketProperties
{
// This must always be equal to the value from the CONNECT packet. So we use it here to ensure that.
AuthenticationMethod = Options.AuthenticationMethod,
AuthenticationMethod = Options.AuthenticationMethod,
AuthenticationData = data.AuthenticationData,
ReasonString = data.ReasonString,
UserProperties = data.UserProperties
@@ -161,6 +181,7 @@ namespace MQTTnet.Client
{
if (options == null) throw new ArgumentNullException(nameof(options));
ThrowIfDisposed();
ThrowIfNotConnected();
var subscribePacket = _adapter.PacketFormatterAdapter.DataConverter.CreateSubscribePacket(options);
@@ -174,6 +195,7 @@ namespace MQTTnet.Client
{
if (options == null) throw new ArgumentNullException(nameof(options));
ThrowIfDisposed();
ThrowIfNotConnected();
var unsubscribePacket = _adapter.PacketFormatterAdapter.DataConverter.CreateUnsubscribePacket(options);
@@ -189,6 +211,7 @@ namespace MQTTnet.Client
MqttTopicValidator.ThrowIfInvalid(applicationMessage.Topic);
ThrowIfDisposed();
ThrowIfNotConnected();
var publishPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePublishPacket(applicationMessage);
@@ -214,17 +237,32 @@ namespace MQTTnet.Client
}
}
public void Dispose()
void Cleanup()
{
_backgroundCancellationTokenSource?.Cancel(false);
_backgroundCancellationTokenSource?.Dispose();
_backgroundCancellationTokenSource = null;
_publishPacketReceiverQueue?.Dispose();
_publishPacketReceiverQueue = null;
_adapter?.Dispose();
_adapter = null;
}
private async Task<MqttClientAuthenticateResult> AuthenticateAsync(IMqttChannelAdapter channelAdapter, MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
protected override void Dispose(bool disposing)
{
if (disposing)
{
Cleanup();
DisconnectedHandler = null;
}
base.Dispose(disposing);
}
async Task<MqttClientAuthenticateResult> AuthenticateAsync(IMqttChannelAdapter channelAdapter, MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
{
var connectPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket(
willApplicationMessage,
@@ -235,7 +273,7 @@ namespace MQTTnet.Client
if (result.ResultCode != MqttClientConnectResultCode.Success)
{
throw new MqttConnectingFailedException(result.ResultCode);
throw new MqttConnectingFailedException(result);
}
_logger.Verbose("Authenticated MQTT connection with server established.");
@@ -243,44 +281,55 @@ namespace MQTTnet.Client
return result;
}
private void ThrowIfNotConnected()
void ThrowIfNotConnected()
{
if (!IsConnected || Interlocked.Read(ref _disconnectGate) == 1) throw new MqttCommunicationException("The client is not connected.");
}
private void ThrowIfConnected(string message)
void ThrowIfConnected(string message)
{
if (IsConnected) throw new MqttProtocolViolationException(message);
}
private async Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult)
async Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult)
{
var clientWasConnected = IsConnected;
TryInitiateDisconnect();
IsConnected = false;
try
{
IsConnected = false;
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(_keepAlivePacketsSenderTask, sender).ConfigureAwait(false);
_logger.Verbose("Disconnected from adapter.");
}
catch (Exception adapterException)
{
_logger.Warning(adapterException, "Error while disconnecting from adapter.");
}
try
{
var receiverTask = WaitForTaskAsync(_packetReceiverTask, sender);
var publishPacketReceiverTask = WaitForTaskAsync(_publishPacketReceiverTask, sender);
var keepAliveTask = WaitForTaskAsync(_keepAlivePacketsSenderTask, sender);
await Task.WhenAll(receiverTask, publishPacketReceiverTask, keepAliveTask).ConfigureAwait(false);
_publishPacketReceiverQueue.Dispose();
}
catch (Exception e)
{
_logger.Warning(e, "Error while waiting for internal tasks.");
}
finally
{
Dispose();
Cleanup();
_cleanDisconnectInitiated = false;
_logger.Info("Disconnected.");
@@ -295,7 +344,7 @@ namespace MQTTnet.Client
}
}
private void TryInitiateDisconnect()
void TryInitiateDisconnect()
{
lock (_disconnectLock)
{
@@ -327,7 +376,7 @@ namespace MQTTnet.Client
return _adapter.SendPacketAsync(packet, Options.CommunicationTimeout, cancellationToken);
}
private async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttBasePacket
async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttBasePacket
{
cancellationToken.ThrowIfCancellationRequested();
@@ -344,17 +393,32 @@ namespace MQTTnet.Client
try
{
await _adapter.SendPacketAsync(requestPacket, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
return await packetAwaiter.WaitOneAsync(Options.CommunicationTimeout).ConfigureAwait(false);
}
catch (MqttCommunicationTimedOutException)
catch (Exception e)
{
_logger.Warning(null, "Timeout while waiting for packet of type '{0}'.", typeof(TResponsePacket).Name);
_logger.Warning(e, "Error when sending packet of type '{0}'.", typeof(TResponsePacket).Name);
packetAwaiter.Cancel();
}
try
{
var response = await packetAwaiter.WaitOneAsync(Options.CommunicationTimeout).ConfigureAwait(false);
_receiveTracker.Restart();
return response;
}
catch (Exception exception)
{
if (exception is MqttCommunicationTimedOutException)
{
_logger.Warning(null, "Timeout while waiting for packet of type '{0}'.", typeof(TResponsePacket).Name);
}
throw;
}
}
}
private async Task TrySendKeepAliveMessagesAsync(CancellationToken cancellationToken)
async Task TrySendKeepAliveMessagesAsync(CancellationToken cancellationToken)
{
try
{
@@ -369,14 +433,14 @@ namespace MQTTnet.Client
keepAliveSendInterval = Options.KeepAliveSendInterval.Value;
}
var waitTime = keepAliveSendInterval - _sendTracker.Elapsed;
if (waitTime <= TimeSpan.Zero)
var waitTimeSend = keepAliveSendInterval - _sendTracker.Elapsed;
var waitTimeReceive = keepAliveSendInterval - _receiveTracker.Elapsed;
if (waitTimeSend <= TimeSpan.Zero || waitTimeReceive <= TimeSpan.Zero)
{
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket(), cancellationToken).ConfigureAwait(false);
waitTime = keepAliveSendInterval;
}
await Task.Delay(waitTime, cancellationToken).ConfigureAwait(false);
await Task.Delay(keepAliveSendInterval, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception exception)
@@ -391,11 +455,11 @@ namespace MQTTnet.Client
}
else if (exception is MqttCommunicationException)
{
_logger.Warning(exception, "MQTT communication exception while sending/receiving keep alive packets.");
_logger.Warning(exception, "Communication error while sending/receiving keep alive packets.");
}
else
{
_logger.Error(exception, "Unhandled exception while sending/receiving keep alive packets.");
_logger.Error(exception, "Error exception while sending/receiving keep alive packets.");
}
if (!DisconnectIsPending())
@@ -409,7 +473,7 @@ namespace MQTTnet.Client
}
}
private async Task TryReceivePacketsAsync(CancellationToken cancellationToken)
async Task TryReceivePacketsAsync(CancellationToken cancellationToken)
{
try
{
@@ -449,11 +513,11 @@ namespace MQTTnet.Client
}
else if (exception is MqttCommunicationException)
{
_logger.Warning(exception, "MQTT communication exception while receiving packets.");
_logger.Warning(exception, "Communication error while receiving packets.");
}
else
{
_logger.Error(exception, "Unhandled exception while receiving packets.");
_logger.Error(exception, "Error while receiving packets.");
}
_packetDispatcher.Dispatch(exception);
@@ -469,13 +533,15 @@ namespace MQTTnet.Client
}
}
private async Task TryProcessReceivedPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
async Task TryProcessReceivedPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
try
{
_receiveTracker.Restart();
if (packet is MqttPublishPacket publishPacket)
{
await TryProcessReceivedPublishPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false);
EnqueueReceivedPublishPacket(publishPacket);
}
else if (packet is MqttPubRelPacket pubRelPacket)
{
@@ -521,11 +587,11 @@ namespace MQTTnet.Client
}
else if (exception is MqttCommunicationException)
{
_logger.Warning(exception, "MQTT communication exception while receiving packets.");
_logger.Warning(exception, "Communication error while receiving packets.");
}
else
{
_logger.Error(exception, "Unhandled exception while receiving packets.");
_logger.Error(exception, "Error while receiving packets.");
}
_packetDispatcher.Dispatch(exception);
@@ -537,64 +603,88 @@ namespace MQTTnet.Client
}
}
private async Task TryProcessReceivedPublishPacketAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
void EnqueueReceivedPublishPacket(MqttPublishPacket publishPacket)
{
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.");
}
_publishPacketReceiverQueue.Enqueue(publishPacket);
}
catch (Exception exception)
{
_logger.Error(exception, "Unhandled exception while handling application message.");
_logger.Error(exception, "Error while enqueueing application message.");
}
}
private async Task<MqttClientPublishResult> PublishAtMostOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
async Task ProcessReceivedPublishPackets(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
var publishPacketDequeueResult = await _publishPacketReceiverQueue.TryDequeueAsync(cancellationToken);
if (!publishPacketDequeueResult.IsSuccess)
{
return;
}
var publishPacket = publishPacketDequeueResult.Item;
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)
{
_logger.Error(exception, "Error while handling application message.");
}
}
}
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)
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)
async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
@@ -611,7 +701,7 @@ namespace MQTTnet.Client
return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(pubRecPacket, pubCompPacket);
}
private async Task<bool> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
async Task<bool> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
{
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
@@ -626,15 +716,25 @@ namespace MQTTnet.Client
return true;
}
private static async Task WaitForTaskAsync(Task task, Task sender)
async Task WaitForTaskAsync(Task task, Task sender)
{
if (task == sender || task == null)
if (task == null)
{
return;
}
if (task.IsCanceled || task.IsCompleted || task.IsFaulted)
if (task == sender)
{
// Return here to avoid deadlocks, but first any eventual exception in the task
// must be handled to avoid not getting an unhandled task exception
if (!task.IsFaulted)
{
return;
}
// By accessing the Exception property the exception is considered handled and will
// not result in an unhandled task exception later by the finalizer
_logger.Warning(task.Exception, "Error while waiting for background task.");
return;
}
@@ -647,9 +747,9 @@ namespace MQTTnet.Client
}
}
private bool DisconnectIsPending()
bool DisconnectIsPending()
{
return Interlocked.CompareExchange(ref _disconnectGate, 1, 0) != 0;
}
}
}
}
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.ExtendedAuthenticationExchange;
using MQTTnet.Client.Options;
@@ -11,6 +7,10 @@ using MQTTnet.Client.Receiving;
using MQTTnet.Client.Subscribing;
using MQTTnet.Client.Unsubscribing;
using MQTTnet.Protocol;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Client
{
@@ -128,7 +128,7 @@ namespace MQTTnet.Client
{
if (client == null) throw new ArgumentNullException(nameof(client));
return client.DisconnectAsync(null);
return client.DisconnectAsync(new MqttClientDisconnectOptions());
}
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, params TopicFilter[] topicFilters)
@@ -1,19 +1,19 @@
using System;
using MQTTnet.Client.ExtendedAuthenticationExchange;
using MQTTnet.Formatter;
using System;
using System.Linq;
using System.Text;
using MQTTnet.Client.ExtendedAuthenticationExchange;
using MQTTnet.Formatter;
namespace MQTTnet.Client.Options
{
public class MqttClientOptionsBuilder
{
private readonly MqttClientOptions _options = new MqttClientOptions();
readonly MqttClientOptions _options = new MqttClientOptions();
private MqttClientTcpOptions _tcpOptions;
private MqttClientWebSocketOptions _webSocketOptions;
private MqttClientOptionsBuilderTlsParameters _tlsParameters;
private MqttClientWebSocketProxyOptions _proxyOptions;
MqttClientTcpOptions _tcpOptions;
MqttClientWebSocketOptions _webSocketOptions;
MqttClientOptionsBuilderTlsParameters _tlsParameters;
MqttClientWebSocketProxyOptions _proxyOptions;
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
{
@@ -256,7 +256,11 @@ namespace MQTTnet.Client.Options
UseTls = true,
SslProtocol = _tlsParameters.SslProtocol,
AllowUntrustedCertificates = _tlsParameters.AllowUntrustedCertificates,
#if WINDOWS_UWP
Certificates = _tlsParameters.Certificates?.Select(c => c.ToArray()).ToList(),
#else
Certificates = _tlsParameters.Certificates?.ToList(),
#endif
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors
@@ -18,7 +18,12 @@ namespace MQTTnet.Client.Options
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
#if WINDOWS_UWP
public IEnumerable<IEnumerable<byte>> Certificates { get; set; }
#else
public IEnumerable<X509Certificate> Certificates { get; set; }
#endif
public bool AllowUntrustedCertificates { get; set; }
@@ -15,8 +15,11 @@ namespace MQTTnet.Client.Options
public bool IgnoreCertificateChainErrors { get; set; }
public bool AllowUntrustedCertificates { get; set; }
#if WINDOWS_UWP
public List<byte[]> Certificates { get; set; }
#else
public List<X509Certificate> Certificates { get; set; }
#endif
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
@@ -0,0 +1,59 @@
using MQTTnet.Packets;
using System;
using System.Collections.Generic;
namespace MQTTnet.Client.Unsubscribing
{
public class MqttClientUnsubscribeOptionsBuilder
{
private readonly MqttClientUnsubscribeOptions _unsubscribeOptions = new MqttClientUnsubscribeOptions();
public MqttClientUnsubscribeOptionsBuilder WithUserProperty(string name, string value)
{
if (name is null) throw new ArgumentNullException(nameof(name));
if (value is null) throw new ArgumentNullException(nameof(value));
return WithUserProperty(new MqttUserProperty(name, value));
}
public MqttClientUnsubscribeOptionsBuilder WithUserProperty(MqttUserProperty userProperty)
{
if (userProperty is null) throw new ArgumentNullException(nameof(userProperty));
if (_unsubscribeOptions.UserProperties is null)
{
_unsubscribeOptions.UserProperties = new List<MqttUserProperty>();
}
_unsubscribeOptions.UserProperties.Add(userProperty);
return this;
}
public MqttClientUnsubscribeOptionsBuilder WithTopicFilter(string topic)
{
if (topic is null) throw new ArgumentNullException(nameof(topic));
if (_unsubscribeOptions.TopicFilters is null)
{
_unsubscribeOptions.TopicFilters = new List<string>();
}
_unsubscribeOptions.TopicFilters.Add(topic);
return this;
}
public MqttClientUnsubscribeOptionsBuilder WithTopicFilter(TopicFilter topicFilter)
{
if (topicFilter is null) throw new ArgumentNullException(nameof(topicFilter));
return WithTopicFilter(topicFilter.Topic);
}
public MqttClientUnsubscribeOptions Build()
{
return _unsubscribeOptions;
}
}
}
@@ -1,17 +0,0 @@
using System;
namespace MQTTnet.Diagnostics
{
public interface IMqttNetChildLogger
{
IMqttNetChildLogger CreateChildLogger(string source = null);
void Verbose(string message, params object[] parameters);
void Info(string message, params object[] parameters);
void Warning(Exception exception, string message, params object[] parameters);
void Error(Exception exception, string message, params object[] parameters);
}
}
+2 -2
View File
@@ -6,8 +6,8 @@ namespace MQTTnet.Diagnostics
{
event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
IMqttNetChildLogger CreateChildLogger(string source = null);
IMqttNetLogger CreateChildLogger(string source);
void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception);
void Publish(MqttNetLogLevel logLevel, string message, object[] parameters, Exception exception);
}
}
@@ -1,51 +0,0 @@
using System;
namespace MQTTnet.Diagnostics
{
public class MqttNetChildLogger : IMqttNetChildLogger
{
private readonly IMqttNetLogger _logger;
private readonly string _source;
public MqttNetChildLogger(IMqttNetLogger logger, string source)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_source = source;
}
public IMqttNetChildLogger CreateChildLogger(string source)
{
string childSource;
if (!string.IsNullOrEmpty(_source))
{
childSource = _source + "." + source;
}
else
{
childSource = source;
}
return new MqttNetChildLogger(_logger, childSource);
}
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);
}
}
}
@@ -4,30 +4,19 @@ namespace MQTTnet.Diagnostics
{
public class MqttNetLogMessage
{
public MqttNetLogMessage(string logId, DateTime timestamp, int threadId, string source, MqttNetLogLevel level, string message, Exception exception)
{
LogId = logId;
Timestamp = timestamp;
ThreadId = threadId;
Source = source;
Level = level;
Message = message;
Exception = exception;
}
public string LogId { get; set; }
public string LogId { get; }
public DateTime Timestamp { get; set; }
public DateTime Timestamp { get; }
public int ThreadId { get; set; }
public int ThreadId { get; }
public string Source { get; set; }
public string Source { get; }
public MqttNetLogLevel Level { get; set; }
public MqttNetLogLevel Level { get; }
public string Message { get; set; }
public string Message { get; }
public Exception Exception { get; }
public Exception Exception { get; set; }
public override string ToString()
{
@@ -6,9 +6,16 @@ namespace MQTTnet.Diagnostics
{
public MqttNetLogMessagePublishedEventArgs(MqttNetLogMessage logMessage)
{
LogMessage = logMessage ?? throw new ArgumentNullException(nameof(logMessage));
#pragma warning disable CS0618 // Type or member is obsolete
TraceMessage = logMessage ?? throw new ArgumentNullException(nameof(logMessage));
#pragma warning restore CS0618 // Type or member is obsolete
}
[Obsolete("Use new proeprty LogMessage instead.")]
public MqttNetLogMessage TraceMessage { get; }
public MqttNetLogMessage LogMessage { get; }
}
}
+52 -9
View File
@@ -4,26 +4,51 @@ namespace MQTTnet.Diagnostics
{
public class MqttNetLogger : IMqttNetLogger
{
private readonly string _logId;
readonly string _logId;
readonly string _source;
public MqttNetLogger(string logId = null)
readonly MqttNetLogger _parentLogger;
public MqttNetLogger(string source, string logId)
{
_source = source;
_logId = logId;
}
public MqttNetLogger()
{
}
public MqttNetLogger(string logId)
{
_logId = logId;
}
MqttNetLogger(MqttNetLogger parentLogger, string logId, string source)
{
_parentLogger = parentLogger ?? throw new ArgumentNullException(nameof(parentLogger));
_source = source ?? throw new ArgumentNullException(nameof(source));
_logId = logId;
}
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
public IMqttNetChildLogger CreateChildLogger(string source = null)
// TODO: Consider creating a LoggerFactory which will allow creating loggers. The logger factory will
// be the only place which has the published event.
public IMqttNetLogger CreateChildLogger(string source)
{
return new MqttNetChildLogger(this, source);
if (source is null) throw new ArgumentNullException(nameof(source));
return new MqttNetLogger(this, _logId, source);
}
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
public void Publish(MqttNetLogLevel level, string message, object[] parameters, Exception exception)
{
var hasLocalListeners = LogMessagePublished != null;
var hasGlobalListeners = MqttNetGlobalLogger.HasListeners;
if (!hasLocalListeners && !hasGlobalListeners)
if (!hasLocalListeners && !hasGlobalListeners && _parentLogger == null)
{
return;
}
@@ -40,17 +65,35 @@ namespace MQTTnet.Diagnostics
}
}
var traceMessage = new MqttNetLogMessage(_logId, DateTime.UtcNow, Environment.CurrentManagedThreadId, source, logLevel, message, exception);
var logMessage = new MqttNetLogMessage
{
LogId = _logId,
Timestamp = DateTime.UtcNow,
Source = _source,
ThreadId = Environment.CurrentManagedThreadId,
Level = level,
Message = message,
Exception = exception
};
if (hasGlobalListeners)
{
MqttNetGlobalLogger.Publish(traceMessage);
MqttNetGlobalLogger.Publish(logMessage);
}
if (hasLocalListeners)
{
LogMessagePublished?.Invoke(this, new MqttNetLogMessagePublishedEventArgs(traceMessage));
LogMessagePublished?.Invoke(this, new MqttNetLogMessagePublishedEventArgs(logMessage));
}
_parentLogger?.Publish(logMessage);
}
void Publish(MqttNetLogMessage logMessage)
{
LogMessagePublished?.Invoke(this, new MqttNetLogMessagePublishedEventArgs(logMessage));
_parentLogger?.Publish(logMessage);
}
}
}
@@ -0,0 +1,35 @@
using System;
namespace MQTTnet.Diagnostics
{
public static class MqttNetLoggerExtensions
{
public static void Verbose(this IMqttNetLogger logger, string message, params object[] parameters)
{
if (logger is null) throw new ArgumentNullException(nameof(logger));
logger.Publish(MqttNetLogLevel.Verbose, message, parameters, null);
}
public static void Info(this IMqttNetLogger logger, string message, params object[] parameters)
{
if (logger is null) throw new ArgumentNullException(nameof(logger));
logger.Publish(MqttNetLogLevel.Info, message, parameters, null);
}
public static void Warning(this IMqttNetLogger logger, Exception exception, string message, params object[] parameters)
{
if (logger is null) throw new ArgumentNullException(nameof(logger));
logger.Publish(MqttNetLogLevel.Warning, message, parameters, exception);
}
public static void Error(this IMqttNetLogger logger, Exception exception, string message, params object[] parameters)
{
if (logger is null) throw new ArgumentNullException(nameof(logger));
logger.Publish(MqttNetLogLevel.Error, message, parameters, exception);
}
}
}
@@ -1,6 +1,6 @@
namespace MQTTnet.Diagnostics
{
public static class TargetFrameworkInfoProvider
public static class TargetFrameworkProvider
{
public static string TargetFramework
{
@@ -0,0 +1,21 @@
using System;
namespace MQTTnet.Exceptions
{
public class MqttConfigurationException : Exception
{
protected MqttConfigurationException()
{
}
public MqttConfigurationException(Exception innerException)
: base(innerException.Message, innerException)
{
}
public MqttConfigurationException(string message)
: base(message)
{
}
}
}
@@ -0,0 +1,48 @@
using System;
using System.Linq;
using MQTTnet.Client.Options;
namespace MQTTnet.Extensions
{
public static class MqttClientOptionsBuilderExtension
{
public static MqttClientOptionsBuilder WithConnectionUri(this MqttClientOptionsBuilder builder, Uri uri)
{
var port = uri.IsDefaultPort ? null : (int?) uri.Port;
switch (uri.Scheme.ToLower())
{
case "tcp":
case "mqtt":
builder.WithTcpServer(uri.Host, port);
break;
case "mqtts":
builder.WithTcpServer(uri.Host, port).WithTls();
break;
case "ws":
case "wss":
builder.WithWebSocketServer(uri.ToString());
break;
default:
throw new ArgumentException("Unexpected scheme in uri.");
}
if (!string.IsNullOrEmpty(uri.UserInfo))
{
var userInfo = uri.UserInfo.Split(':');
var username = userInfo[0];
var password = userInfo.Length > 1 ? userInfo[1] : "";
builder.WithCredentials(username, password);
}
return builder;
}
public static MqttClientOptionsBuilder WithConnectionUri(this MqttClientOptionsBuilder builder, string uri)
{
return WithConnectionUri(builder, new Uri(uri, UriKind.Absolute));
}
}
}
@@ -0,0 +1,16 @@
using System;
using System.Linq;
namespace MQTTnet.Extensions
{
public static class UserPropertyExtension
{
public static string GetUserProperty(this MqttApplicationMessage message, string propertyName, StringComparison comparisonType = StringComparison.Ordinal)
{
if (message == null) throw new ArgumentNullException(nameof(message));
if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));
return message.UserProperties?.SingleOrDefault(up => up.Name.Equals(propertyName, comparisonType))?.Value;
}
}
}
@@ -1,6 +1,4 @@
using System;
using System.Linq;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Publishing;
@@ -10,6 +8,8 @@ using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System;
using System.Linq;
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
namespace MQTTnet.Formatter.V3
@@ -20,11 +20,6 @@ namespace MQTTnet.Formatter.V3
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
if (applicationMessage.UserProperties?.Any() == true)
{
throw new MqttProtocolViolationException("User properties are not supported in MQTT version 3.");
}
return new MqttPublishPacket
{
Topic = applicationMessage.Topic,
@@ -158,7 +153,7 @@ namespace MQTTnet.Formatter.V3
{
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
if (unsubAckPacket == null) throw new ArgumentNullException(nameof(unsubAckPacket));
var result = new MqttClientUnsubscribeResult();
result.Items.AddRange(unsubscribePacket.TopicFilters.Select((t, i) =>
@@ -171,14 +166,9 @@ namespace MQTTnet.Formatter.V3
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options.UserProperties?.Any() == true)
{
throw new MqttProtocolViolationException("User properties are not supported in MQTT version 3.");
}
var subscribePacket = new MqttSubscribePacket();
subscribePacket.TopicFilters.AddRange(options.TopicFilters);
return subscribePacket;
}
@@ -186,11 +176,6 @@ namespace MQTTnet.Formatter.V3
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options.UserProperties?.Any() == true)
{
throw new MqttProtocolViolationException("User properties are not supported in MQTT version 3.");
}
var unsubscribePacket = new MqttUnsubscribePacket();
unsubscribePacket.TopicFilters.AddRange(options.TopicFilters);
@@ -199,9 +184,9 @@ namespace MQTTnet.Formatter.V3
public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
{
if (options != null)
if (options.ReasonCode != MqttClientDisconnectReason.NormalDisconnection || options.ReasonString != null)
{
throw new MqttProtocolViolationException("Reason codes for disconnect are only supported for MQTTv5.");
throw new MqttProtocolViolationException("Reason codes and reason string for disconnect are only supported for MQTTv5.");
}
return new MqttDisconnectPacket();
@@ -139,7 +139,7 @@ namespace MQTTnet.Formatter.V5
ReasonCode = connectionValidatorContext.ReasonCode,
Properties = new MqttConnAckPacketProperties
{
UserProperties = connectionValidatorContext.UserProperties,
UserProperties = connectionValidatorContext.ResponseUserProperties,
AuthenticationMethod = connectionValidatorContext.AuthenticationMethod,
AuthenticationData = connectionValidatorContext.ResponseAuthenticationData,
AssignedClientIdentifier = connectionValidatorContext.AssignedClientIdentifier,
@@ -0,0 +1,229 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Implementations
{
public sealed class CrossPlatformSocket : IDisposable
{
readonly Socket _socket;
public CrossPlatformSocket(AddressFamily addressFamily)
{
_socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
}
public CrossPlatformSocket()
{
// Having this contructor is important because avoiding the address family as parameter
// will make use of dual mode in the .net framework.
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
}
public CrossPlatformSocket(Socket socket)
{
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
}
public bool NoDelay
{
get
{
return (int)_socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay) > 0;
}
set
{
_socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
}
}
public bool DualMode
{
get
{
return _socket.DualMode;
}
set
{
_socket.DualMode = value;
}
}
public int ReceiveBufferSize
{
get
{
return _socket.ReceiveBufferSize;
}
set
{
_socket.ReceiveBufferSize = value;
}
}
public int SendBufferSize
{
get
{
return _socket.SendBufferSize;
}
set
{
_socket.SendBufferSize = value;
}
}
public EndPoint RemoteEndPoint
{
get
{
return _socket.RemoteEndPoint;
}
}
public bool ReuseAddress
{
get
{
return (int)_socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress) != 0;
}
set
{
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, value ? 1 : 0);
}
}
public async Task<CrossPlatformSocket> AcceptAsync()
{
try
{
#if NET452 || NET461
var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null).ConfigureAwait(false);
return new CrossPlatformSocket(clientSocket);
#else
var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false);
return new CrossPlatformSocket(clientSocket);
#endif
}
catch (ObjectDisposedException)
{
// This will happen when _socket.EndAccept gets called by Task library but the socket is already disposed.
return null;
}
}
public void Bind(EndPoint localEndPoint)
{
if (localEndPoint is null) throw new ArgumentNullException(nameof(localEndPoint));
_socket.Bind(localEndPoint);
}
public void Listen(int connectionBacklog)
{
_socket.Listen(connectionBacklog);
}
public async Task ConnectAsync(string host, int port, CancellationToken cancellationToken)
{
if (host is null) throw new ArgumentNullException(nameof(host));
try
{
// Workaround for: workaround for https://github.com/dotnet/corefx/issues/24430
using (cancellationToken.Register(() => _socket.Dispose()))
{
cancellationToken.ThrowIfCancellationRequested();
#if NET452 || NET461
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, host, port, null).ConfigureAwait(false);
#else
await _socket.ConnectAsync(host, port).ConfigureAwait(false);
#endif
}
}
catch (ObjectDisposedException)
{
// This will happen when _socket.EndConnect gets called by Task library but the socket is already disposed.
}
}
public async Task SendAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
{
try
{
#if NET452 || NET461
await Task.Factory.FromAsync(SocketWrapper.BeginSend, _socket.EndSend, new SocketWrapper(_socket, buffer, socketFlags)).ConfigureAwait(false);
#else
await _socket.SendAsync(buffer, socketFlags).ConfigureAwait(false);
#endif
}
catch (ObjectDisposedException)
{
// This will happen when _socket.EndConnect gets called by Task library but the socket is already disposed.
}
}
public async Task<int> ReceiveAsync(ArraySegment<byte> buffer, SocketFlags socketFlags)
{
try
{
#if NET452 || NET461
return await Task.Factory.FromAsync(SocketWrapper.BeginReceive, _socket.EndReceive, new SocketWrapper(_socket, buffer, socketFlags)).ConfigureAwait(false);
#else
return await _socket.ReceiveAsync(buffer, socketFlags).ConfigureAwait(false);
#endif
}
catch (ObjectDisposedException)
{
// This will happen when _socket.EndReceive gets called by Task library but the socket is already disposed.
return -1;
}
}
public NetworkStream GetStream()
{
return new NetworkStream(_socket, true);
}
public void Dispose()
{
_socket?.Dispose();
}
#if NET452 || NET461
class SocketWrapper
{
readonly Socket _socket;
readonly ArraySegment<byte> _buffer;
readonly SocketFlags _socketFlags;
public SocketWrapper(Socket socket, ArraySegment<byte> buffer, SocketFlags socketFlags)
{
_socket = socket;
_buffer = buffer;
_socketFlags = socketFlags;
}
public static IAsyncResult BeginSend(AsyncCallback callback, object state)
{
var socketWrapper = (SocketWrapper)state;
return socketWrapper._socket.BeginSend(socketWrapper._buffer.Array, socketWrapper._buffer.Offset, socketWrapper._buffer.Count, socketWrapper._socketFlags, callback, state);
}
public static IAsyncResult BeginReceive(AsyncCallback callback, object state)
{
var socketWrapper = (SocketWrapper)state;
return socketWrapper._socket.BeginReceive(socketWrapper._buffer.Array, socketWrapper._buffer.Offset, socketWrapper._buffer.Count, socketWrapper._socketFlags, callback, state);
}
}
#endif
}
}
@@ -1,17 +1,17 @@
using System;
using MQTTnet.Adapter;
using MQTTnet.Adapter;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using System;
namespace MQTTnet.Implementations
{
public class MqttClientAdapterFactory : IMqttClientAdapterFactory
{
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
switch (options.ChannelOptions)
{
case MqttClientTcpOptions _:
@@ -1,24 +1,24 @@
#if !WINDOWS_UWP
using System;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System.IO;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
using MQTTnet.Channel;
using MQTTnet.Client.Options;
using System;
using System.IO;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Runtime.ExceptionServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Implementations
{
public class MqttTcpChannel : IMqttChannel
public sealed class MqttTcpChannel : IDisposable, IMqttChannel
{
private readonly IMqttClientOptions _clientOptions;
private readonly MqttClientTcpOptions _options;
readonly IMqttClientOptions _clientOptions;
readonly MqttClientTcpOptions _options;
private Stream _stream;
Stream _stream;
public MqttTcpChannel(IMqttClientOptions clientOptions)
{
@@ -46,15 +46,15 @@ namespace MQTTnet.Implementations
public async Task ConnectAsync(CancellationToken cancellationToken)
{
Socket socket;
CrossPlatformSocket socket;
if (_options.AddressFamily == AddressFamily.Unspecified)
{
socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket = new CrossPlatformSocket();
}
else
{
socket = new Socket(_options.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket = new CrossPlatformSocket(_options.AddressFamily);
}
socket.ReceiveBufferSize = _options.BufferSize;
@@ -68,25 +68,25 @@ namespace MQTTnet.Implementations
// of the actual value.
socket.DualMode = _options.DualMode.Value;
}
// Workaround for: workaround for https://github.com/dotnet/corefx/issues/24430
using (cancellationToken.Register(() => socket.Dispose()))
{
#if NET452 || NET461
await Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, _options.Server, _options.GetPort(), null).ConfigureAwait(false);
#else
await socket.ConnectAsync(_options.Server, _options.GetPort()).ConfigureAwait(false);
#endif
}
var networkStream = new NetworkStream(socket, true);
await socket.ConnectAsync(_options.Server, _options.GetPort(), cancellationToken).ConfigureAwait(false);
var networkStream = socket.GetStream();
if (_options.TlsOptions.UseTls)
{
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
_stream = sslStream;
try
{
await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, !_options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
}
catch
{
sslStream.Dispose();
throw;
}
await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, !_options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
_stream = sslStream;
}
else
{
@@ -104,19 +104,22 @@ namespace MQTTnet.Implementations
public async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer is null) throw new ArgumentNullException(nameof(buffer));
try
{
// Workaround for: https://github.com/dotnet/corefx/issues/24430
using (cancellationToken.Register(Dispose))
{
if (cancellationToken.IsCancellationRequested)
{
return 0;
}
cancellationToken.ThrowIfCancellationRequested();
return await _stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
}
}
catch (ObjectDisposedException)
{
return -1;
}
catch (IOException exception)
{
if (exception.InnerException is SocketException socketException)
@@ -130,19 +133,22 @@ namespace MQTTnet.Implementations
public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer is null) throw new ArgumentNullException(nameof(buffer));
try
{
// Workaround for: https://github.com/dotnet/corefx/issues/24430
using (cancellationToken.Register(Dispose))
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
cancellationToken.ThrowIfCancellationRequested();
await _stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
}
}
catch (ObjectDisposedException)
{
return;
}
catch (IOException exception)
{
if (exception.InnerException is SocketException socketException)
@@ -173,7 +179,7 @@ namespace MQTTnet.Implementations
_stream = null;
}
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (_options.TlsOptions.CertificateValidationCallback != null)
{
@@ -204,7 +210,7 @@ namespace MQTTnet.Implementations
return _options.TlsOptions.AllowUntrustedCertificates;
}
private X509CertificateCollection LoadCertificates()
X509CertificateCollection LoadCertificates()
{
var certificates = new X509CertificateCollection();
if (_options.TlsOptions.Certificates == null)
@@ -214,7 +220,7 @@ namespace MQTTnet.Implementations
foreach (var certificate in _options.TlsOptions.Certificates)
{
certificates.Add(new X509Certificate2(certificate));
certificates.Add(certificate);
}
return certificates;
@@ -11,14 +11,14 @@ using System.Threading.Tasks;
namespace MQTTnet.Implementations
{
public class MqttTcpServerAdapter : IMqttServerAdapter
public sealed class MqttTcpServerAdapter : IMqttServerAdapter
{
private readonly IMqttNetChildLogger _logger;
readonly IMqttNetLogger _logger;
private IMqttServerOptions _options;
private StreamSocketListener _listener;
IMqttServerOptions _options;
StreamSocketListener _listener;
public MqttTcpServerAdapter(IMqttNetChildLogger logger)
public MqttTcpServerAdapter(IMqttNetLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
@@ -68,7 +68,7 @@ namespace MQTTnet.Implementations
_listener = null;
}
private async void OnConnectionReceivedAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
async void OnConnectionReceivedAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
try
{
@@ -1,4 +1,8 @@
#if !WINDOWS_UWP
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Internal;
using MQTTnet.Server;
using System;
using System.Collections.Generic;
using System.Net;
@@ -6,20 +10,17 @@ using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Server;
namespace MQTTnet.Implementations
{
public class MqttTcpServerAdapter : IMqttServerAdapter
public sealed class MqttTcpServerAdapter : Disposable, IMqttServerAdapter
{
private readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>();
private readonly IMqttNetChildLogger _logger;
readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>();
readonly IMqttNetLogger _logger;
private CancellationTokenSource _cancellationTokenSource;
CancellationTokenSource _cancellationTokenSource;
public MqttTcpServerAdapter(IMqttNetChildLogger logger)
public MqttTcpServerAdapter(IMqttNetLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
@@ -58,7 +59,7 @@ namespace MQTTnet.Implementations
{
tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate, options.TlsEndpointOptions.CertificateCredentials.Password);
}
if (!tlsCertificate.HasPrivateKey)
{
throw new InvalidOperationException("The certificate for TLS encryption must contain the private key.");
@@ -72,11 +73,21 @@ namespace MQTTnet.Implementations
public Task StopAsync()
{
Dispose();
Cleanup();
return Task.FromResult(0);
}
public void Dispose()
protected override void Dispose(bool disposing)
{
if (disposing)
{
Cleanup();
}
base.Dispose(disposing);
}
void Cleanup()
{
_cancellationTokenSource?.Cancel(false);
_cancellationTokenSource?.Dispose();
@@ -90,7 +101,7 @@ namespace MQTTnet.Implementations
_listeners.Clear();
}
private void RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate, CancellationToken cancellationToken)
void RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate, CancellationToken cancellationToken)
{
if (!options.BoundInterNetworkAddress.Equals(IPAddress.None))
{
@@ -1,4 +1,9 @@
#if !WINDOWS_UWP
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Server;
using System;
using System.IO;
using System.Net;
@@ -7,30 +12,25 @@ using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Server;
namespace MQTTnet.Implementations
{
public class MqttTcpServerListener : IDisposable
public sealed class MqttTcpServerListener : IDisposable
{
private readonly IMqttNetChildLogger _logger;
private readonly AddressFamily _addressFamily;
private readonly MqttServerTcpEndpointBaseOptions _options;
private readonly MqttServerTlsTcpEndpointOptions _tlsOptions;
private readonly X509Certificate2 _tlsCertificate;
readonly IMqttNetLogger _logger;
readonly AddressFamily _addressFamily;
readonly MqttServerTcpEndpointBaseOptions _options;
readonly MqttServerTlsTcpEndpointOptions _tlsOptions;
readonly X509Certificate2 _tlsCertificate;
private Socket _socket;
private CrossPlatformSocket _socket;
private IPEndPoint _localEndPoint;
public MqttTcpServerListener(
AddressFamily addressFamily,
MqttServerTcpEndpointBaseOptions options,
X509Certificate2 tlsCertificate,
IMqttNetChildLogger logger)
IMqttNetLogger logger)
{
_addressFamily = addressFamily;
_options = options;
@@ -59,20 +59,20 @@ namespace MQTTnet.Implementations
_logger.Info($"Starting TCP listener for {_localEndPoint} TLS={_tlsCertificate != null}.");
_socket = new Socket(_addressFamily, SocketType.Stream, ProtocolType.Tcp);
_socket = new CrossPlatformSocket(_addressFamily);
// Usage of socket options is described here: https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.setsocketoption?view=netcore-2.2
if (_options.ReuseAddress)
{
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_socket.ReuseAddress = true;
}
if (_options.NoDelay)
{
_socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
_socket.NoDelay = true;
}
_socket.Bind(_localEndPoint);
_socket.Listen(_options.ConnectionBacklog);
@@ -87,7 +87,7 @@ namespace MQTTnet.Implementations
throw;
}
_logger.Warning(exception,"Error while creating listener socket for local end point '{0}'.", _localEndPoint);
_logger.Warning(exception, "Error while creating listener socket for local end point '{0}'.", _localEndPoint);
return false;
}
}
@@ -101,18 +101,13 @@ namespace MQTTnet.Implementations
#endif
}
private async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken)
async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
#if NET452 || NET461
var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null).ConfigureAwait(false);
#else
var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false);
#endif
if (clientSocket == null)
{
continue;
@@ -121,7 +116,7 @@ namespace MQTTnet.Implementations
Task.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken).Forget(_logger);
}
catch (OperationCanceledException)
{
{
}
catch (Exception exception)
{
@@ -133,14 +128,14 @@ namespace MQTTnet.Implementations
continue;
}
}
_logger.Error(exception, $"Error while accepting connection at TCP listener {_localEndPoint} TLS={_tlsCertificate != null}.");
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
}
}
}
private async Task TryHandleClientConnectionAsync(Socket clientSocket)
async Task TryHandleClientConnectionAsync(CrossPlatformSocket clientSocket)
{
Stream stream = null;
string remoteEndPoint = null;
@@ -156,7 +151,7 @@ namespace MQTTnet.Implementations
clientSocket.NoDelay = _options.NoDelay;
stream = new NetworkStream(clientSocket, true);
stream = clientSocket.GetStream();
X509Certificate2 clientCertificate = null;
@@ -165,9 +160,9 @@ namespace MQTTnet.Implementations
var sslStream = new SslStream(stream, false, _tlsOptions.RemoteCertificateValidationCallback);
await sslStream.AuthenticateAsServerAsync(
_tlsCertificate,
_tlsOptions.ClientCertificateRequired,
_tlsOptions.SslProtocol,
_tlsCertificate,
_tlsOptions.ClientCertificateRequired,
_tlsOptions.SslProtocol,
_tlsOptions.CheckCertificateRevocation).ConfigureAwait(false);
stream = sslStream;
@@ -6,10 +6,11 @@ using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Client.Options;
using MQTTnet.Internal;
namespace MQTTnet.Implementations
{
public class MqttWebSocketChannel : IMqttChannel
public class MqttWebSocketChannel : Disposable, IMqttChannel
{
private readonly MqttClientWebSocketOptions _options;
@@ -84,7 +85,12 @@ namespace MQTTnet.Implementations
clientWebSocket.Options.ClientCertificates = new X509CertificateCollection();
foreach (var certificate in _options.TlsOptions.Certificates)
{
#if WINDOWS_UWP
clientWebSocket.Options.ClientCertificates.Add(new X509Certificate(certificate));
#else
clientWebSocket.Options.ClientCertificates.Add(certificate);
#endif
}
}
@@ -106,7 +112,7 @@ namespace MQTTnet.Implementations
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
}
Dispose();
Cleanup();
}
public async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
@@ -136,7 +142,16 @@ namespace MQTTnet.Implementations
}
}
public void Dispose()
protected override void Dispose(bool disposing)
{
if (disposing)
{
Cleanup();
}
base.Dispose(disposing);
}
private void Cleanup()
{
_sendLock?.Dispose();
_sendLock = null;
@@ -0,0 +1,20 @@
using System.Threading.Tasks;
namespace MQTTnet.Implementations
{
public static class PlatformAbstractionLayer
{
public static Task CompletedTask
{
get
{
#if NET452
return Task.FromResult(0);
#else
return Task.CompletedTask;
#endif
}
}
}
}
@@ -1,131 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Internal
{
// Inspired from Stephen Toub (https://blogs.msdn.microsoft.com/pfxteam/2012/02/11/building-async-coordination-primitives-part-2-asyncautoresetevent/) and Chris Gillum (https://stackoverflow.com/a/43012490)
public class AsyncAutoResetEvent
{
private readonly LinkedList<TaskCompletionSource<bool>> _waiters = new LinkedList<TaskCompletionSource<bool>>();
private bool _isSignaled;
public AsyncAutoResetEvent()
: this(false)
{
}
public AsyncAutoResetEvent(bool signaled)
{
_isSignaled = signaled;
}
public int WaitersCount
{
get
{
lock (_waiters)
{
return _waiters.Count;
}
}
}
public Task<bool> WaitOneAsync()
{
return WaitOneAsync(CancellationToken.None);
}
public Task<bool> WaitOneAsync(TimeSpan timeout)
{
return WaitOneAsync(timeout, CancellationToken.None);
}
public Task<bool> WaitOneAsync(CancellationToken cancellationToken)
{
return WaitOneAsync(Timeout.InfiniteTimeSpan, cancellationToken);
}
public async Task<bool> WaitOneAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
TaskCompletionSource<bool> tcs;
lock (_waiters)
{
if (_isSignaled)
{
_isSignaled = false;
return true;
}
if (timeout == TimeSpan.Zero)
{
return _isSignaled;
}
tcs = new TaskCompletionSource<bool>();
_waiters.AddLast(tcs);
}
Task winner;
if (timeout == Timeout.InfiniteTimeSpan)
{
using (cancellationToken.Register(() => { tcs.TrySetCanceled(); }))
{
await tcs.Task.ConfigureAwait(false);
winner = tcs.Task;
}
}
else
{
winner = await Task.WhenAny(tcs.Task, Task.Delay(timeout, cancellationToken)).ConfigureAwait(false);
}
var taskWasSignaled = winner == tcs.Task;
if (taskWasSignaled)
{
return true;
}
// We timed-out; remove our reference to the task.
// This is an O(n) operation since waiters is a LinkedList<T>.
lock (_waiters)
{
_waiters.Remove(tcs);
if (winner.Status == TaskStatus.Canceled)
{
throw new OperationCanceledException(cancellationToken);
}
throw new TimeoutException();
}
}
public void Set()
{
TaskCompletionSource<bool> toRelease = null;
lock (_waiters)
{
if (_waiters.Count > 0)
{
// Signal the first task in the waiters list.
toRelease = _waiters.First.Value;
_waiters.RemoveFirst();
}
else if (!_isSignaled)
{
// No tasks are pending
_isSignaled = true;
}
}
toRelease?.TrySetResult(true);
}
}
}
@@ -23,9 +23,15 @@ namespace MQTTnet.Internal
{
while (!cancellationToken.IsCancellationRequested)
{
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
try
{
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
}
catch (OperationCanceledException)
{
return new AsyncQueueDequeueResult<TItem>(false, default(TItem));
}
if (_queue.TryDequeue(out var item))
{
+15 -6
View File
@@ -4,11 +4,11 @@ using System.Threading;
namespace MQTTnet.Internal
{
public class BlockingQueue<TItem>
public class BlockingQueue<TItem> : Disposable
{
private readonly object _syncRoot = new object();
private readonly LinkedList<TItem> _items = new LinkedList<TItem>();
private readonly ManualResetEvent _gate = new ManualResetEvent(false);
private readonly ManualResetEventSlim _gate = new ManualResetEventSlim(false);
public int Count
{
@@ -32,7 +32,7 @@ namespace MQTTnet.Internal
}
}
public TItem Dequeue()
public TItem Dequeue(CancellationToken cancellationToken = default(CancellationToken))
{
while (true)
{
@@ -52,11 +52,11 @@ namespace MQTTnet.Internal
}
}
_gate.WaitOne();
_gate.Wait(cancellationToken);
}
}
public TItem PeekAndWait()
public TItem PeekAndWait(CancellationToken cancellationToken = default(CancellationToken))
{
while (true)
{
@@ -73,7 +73,7 @@ namespace MQTTnet.Internal
}
}
_gate.WaitOne();
_gate.Wait(cancellationToken);
}
}
@@ -108,5 +108,14 @@ namespace MQTTnet.Internal
_items.Clear();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_gate.Dispose();
}
base.Dispose(disposing);
}
}
}
+44
View File
@@ -0,0 +1,44 @@
using System;
namespace MQTTnet.Internal
{
public abstract class Disposable : IDisposable
{
protected bool IsDisposed { get; private set; } = false;
protected void ThrowIfDisposed()
{
if (IsDisposed)
{
throw new ObjectDisposedException(GetType().Name);
}
}
protected virtual void Dispose(bool disposing)
{
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~Disposable()
// {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
if (IsDisposed)
{
return;
}
IsDisposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
+3 -3
View File
@@ -1,11 +1,11 @@
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
using MQTTnet.Diagnostics;
using System.Threading.Tasks;
namespace MQTTnet.Internal
{
public static class TaskExtensions
{
public static void Forget(this Task task, IMqttNetChildLogger logger)
public static void Forget(this Task task, IMqttNetLogger logger)
{
task?.ContinueWith(t =>
{
@@ -0,0 +1,19 @@
using MQTTnet.Client.Options;
using MQTTnet.Packets;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.LowLevelClient
{
public interface ILowLevelMqttClient : IDisposable
{
Task ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken);
Task DisconnectAsync(CancellationToken cancellationToken);
Task SendAsync(MqttBasePacket packet, CancellationToken cancellationToken);
Task<MqttBasePacket> ReceiveAsync(CancellationToken cancellationToken);
}
}
@@ -0,0 +1,128 @@
using MQTTnet.Adapter;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Packets;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.LowLevelClient
{
public sealed class LowLevelMqttClient : ILowLevelMqttClient
{
readonly IMqttNetLogger _logger;
readonly IMqttClientAdapterFactory _clientAdapterFactory;
IMqttChannelAdapter _adapter;
IMqttClientOptions _options;
public LowLevelMqttClient(IMqttClientAdapterFactory clientAdapterFactory, IMqttNetLogger logger)
{
if (clientAdapterFactory is null) throw new ArgumentNullException(nameof(clientAdapterFactory));
if (logger is null) throw new ArgumentNullException(nameof(logger));
_clientAdapterFactory = clientAdapterFactory;
_logger = logger.CreateChildLogger(nameof(LowLevelMqttClient));
}
bool IsConnected => _adapter != null;
public async Task ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken)
{
if (options is null) throw new ArgumentNullException(nameof(options));
if (_adapter != null)
{
throw new InvalidOperationException("Low level MQTT client is already connected. Disconnect first before connecting again.");
}
var newAdapter = _clientAdapterFactory.CreateClientAdapter(options, _logger);
try
{
_logger.Verbose($"Trying to connect with server '{options.ChannelOptions}' (Timeout={options.CommunicationTimeout}).");
await newAdapter.ConnectAsync(options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
_logger.Verbose("Connection with server established.");
_options = options;
}
catch (Exception)
{
_adapter.Dispose();
throw;
}
_adapter = newAdapter;
}
public async Task DisconnectAsync(CancellationToken cancellationToken)
{
if (_adapter == null)
{
return;
}
await SafeDisconnect(cancellationToken).ConfigureAwait(false);
_adapter = null;
}
public async Task SendAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
if (packet is null) throw new ArgumentNullException(nameof(packet));
if (_adapter == null)
{
throw new InvalidOperationException("Low level MQTT client is not connected.");
}
try
{
await _adapter.SendPacketAsync(packet, _options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
}
catch (Exception)
{
await SafeDisconnect(cancellationToken).ConfigureAwait(false);
throw;
}
}
public async Task<MqttBasePacket> ReceiveAsync(CancellationToken cancellationToken)
{
if (_adapter == null)
{
throw new InvalidOperationException("Low level MQTT client is not connected.");
}
try
{
return await _adapter.ReceivePacketAsync(_options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
}
catch (Exception)
{
await SafeDisconnect(cancellationToken).ConfigureAwait(false);
throw;
}
}
public void Dispose()
{
_adapter?.Dispose();
}
async Task SafeDisconnect(CancellationToken cancellationToken)
{
try
{
await _adapter.DisconnectAsync(_options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error(exception, "Error while disconnecting.");
}
finally
{
_adapter.Dispose();
}
}
}
}
+4
View File
@@ -62,5 +62,9 @@
<ItemGroup Condition="'$(TargetFramework)'=='net461'">
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.10" />
</ItemGroup>
</Project>
+37 -9
View File
@@ -1,16 +1,17 @@
using System;
using System.Collections.Generic;
using MQTTnet.Adapter;
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Implementations;
using MQTTnet.LowLevelClient;
using MQTTnet.Server;
using System;
using System.Collections.Generic;
namespace MQTTnet
{
public class MqttFactory : IMqttFactory
public sealed class MqttFactory : IMqttFactory
{
private IMqttClientAdapterFactory _clientAdapterFactory = new MqttClientAdapterFactory();
IMqttClientAdapterFactory _clientAdapterFactory = new MqttClientAdapterFactory();
public MqttFactory() : this(new MqttNetLogger())
{
@@ -29,6 +30,33 @@ namespace MQTTnet
return this;
}
public ILowLevelMqttClient CreateLowLevelMqttClient()
{
return CreateLowLevelMqttClient(DefaultLogger);
}
public ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
return new LowLevelMqttClient(_clientAdapterFactory, logger);
}
public ILowLevelMqttClient CreateLowLevelMqttClient(IMqttClientAdapterFactory clientAdapterFactory)
{
if (clientAdapterFactory == null) throw new ArgumentNullException(nameof(clientAdapterFactory));
return new LowLevelMqttClient(_clientAdapterFactory, DefaultLogger);
}
public ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory clientAdapterFactoryy)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
if (clientAdapterFactoryy == null) throw new ArgumentNullException(nameof(clientAdapterFactoryy));
return new LowLevelMqttClient(_clientAdapterFactory, logger);
}
public IMqttClient CreateMqttClient()
{
return CreateMqttClient(DefaultLogger);
@@ -65,7 +93,7 @@ namespace MQTTnet
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
return CreateMqttServer(new List<IMqttServerAdapter> { new MqttTcpServerAdapter(logger.CreateChildLogger()) }, logger);
return CreateMqttServer(new List<IMqttServerAdapter> { new MqttTcpServerAdapter(logger) }, logger);
}
public IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> serverAdapters, IMqttNetLogger logger)
@@ -73,14 +101,14 @@ namespace MQTTnet
if (serverAdapters == null) throw new ArgumentNullException(nameof(serverAdapters));
if (logger == null) throw new ArgumentNullException(nameof(logger));
return new MqttServer(serverAdapters, logger.CreateChildLogger());
return new MqttServer(serverAdapters, logger);
}
public IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> serverAdapters)
{
if (serverAdapters == null) throw new ArgumentNullException(nameof(serverAdapters));
return new MqttServer(serverAdapters, DefaultLogger.CreateChildLogger());
return new MqttServer(serverAdapters, DefaultLogger);
}
}
}
@@ -1,28 +1,34 @@
using System;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Packets;
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
namespace MQTTnet.PacketDispatcher
{
public sealed class MqttPacketAwaiter<TPacket> : IMqttPacketAwaiter where TPacket : MqttBasePacket
public sealed class MqttPacketAwaiter<TPacket> : Disposable, IMqttPacketAwaiter where TPacket : MqttBasePacket
{
private readonly TaskCompletionSource<MqttBasePacket> _taskCompletionSource = new TaskCompletionSource<MqttBasePacket>();
private readonly TaskCompletionSource<MqttBasePacket> _taskCompletionSource;
private readonly ushort? _packetIdentifier;
private readonly MqttPacketDispatcher _owningPacketDispatcher;
public MqttPacketAwaiter(ushort? packetIdentifier, MqttPacketDispatcher owningPacketDispatcher)
{
_packetIdentifier = packetIdentifier;
_owningPacketDispatcher = owningPacketDispatcher ?? throw new ArgumentNullException(nameof(owningPacketDispatcher));
#if NET452
_taskCompletionSource = new TaskCompletionSource<MqttBasePacket>();
#else
_taskCompletionSource = new TaskCompletionSource<MqttBasePacket>(TaskCreationOptions.RunContinuationsAsynchronously);
#endif
}
public async Task<TPacket> WaitOneAsync(TimeSpan timeout)
{
using (var timeoutToken = new CancellationTokenSource(timeout))
{
timeoutToken.Token.Register(() => _taskCompletionSource.TrySetException(new MqttCommunicationTimedOutException()));
timeoutToken.Token.Register(() => Fail(new MqttCommunicationTimedOutException()));
var packet = await _taskCompletionSource.Task.ConfigureAwait(false);
return (TPacket)packet;
@@ -32,29 +38,57 @@ namespace MQTTnet.PacketDispatcher
public void Complete(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
#if NET452
// To prevent deadlocks it is required to call the _TrySetResult_ method
// from a new thread because the awaiting code will not(!) be executed in
// a new thread automatically (due to await). Furthermore _this_ thread will
// do it. But _this_ thread is also reading incoming packets -> deadlock.
// NET452 does not support RunContinuationsAsynchronously
Task.Run(() => _taskCompletionSource.TrySetResult(packet));
#else
_taskCompletionSource.TrySetResult(packet);
#endif
}
public void Fail(Exception exception)
{
if (exception == null) throw new ArgumentNullException(nameof(exception));
#if NET452
// To prevent deadlocks it is required to call the _TrySetResult_ method
// from a new thread because the awaiting code will not(!) be executed in
// a new thread automatically (due to await). Furthermore _this_ thread will
// do it. But _this_ thread is also reading incoming packets -> deadlock.
// NET452 does not support RunContinuationsAsynchronously
Task.Run(() => _taskCompletionSource.TrySetException(exception));
#else
_taskCompletionSource.TrySetException(exception);
#endif
}
public void Cancel()
{
#if NET452
// To prevent deadlocks it is required to call the _TrySetResult_ method
// from a new thread because the awaiting code will not(!) be executed in
// a new thread automatically (due to await). Furthermore _this_ thread will
// do it. But _this_ thread is also reading incoming packets -> deadlock.
// NET452 does not support RunContinuationsAsynchronously
Task.Run(() => _taskCompletionSource.TrySetCanceled());
#else
_taskCompletionSource.TrySetCanceled();
#endif
}
public void Dispose()
protected override void Dispose(bool disposing)
{
_owningPacketDispatcher.RemovePacketAwaiter<TPacket>(_packetIdentifier);
if (disposing)
{
_owningPacketDispatcher.RemovePacketAwaiter<TPacket>(_packetIdentifier);
}
base.Dispose(disposing);
}
}
}
@@ -0,0 +1,21 @@
using MQTTnet.Diagnostics;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Server
{
public interface IMqttRetainedMessagesManager
{
Task Start(IMqttServerOptions options, IMqttNetLogger logger);
Task LoadMessagesAsync();
Task ClearMessagesAsync();
Task HandleMessageAsync(string clientId, MqttApplicationMessage applicationMessage);
Task<IList<MqttApplicationMessage>> GetMessagesAsync();
Task<IList<MqttApplicationMessage>> GetSubscribedMessagesAsync(ICollection<TopicFilter> topicFilters);
}
}
+4 -3
View File
@@ -1,11 +1,12 @@
using System.Collections.Generic;
using MQTTnet.Server.Status;
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Server.Status;
namespace MQTTnet.Server
{
public interface IMqttServer : IApplicationMessageReceiver, IApplicationMessagePublisher
{
bool IsStarted { get; }
IMqttServerStartedHandler StartedHandler { get; set; }
IMqttServerStoppedHandler StoppedHandler { get; set; }
@@ -13,7 +14,7 @@ namespace MQTTnet.Server
IMqttServerClientDisconnectedHandler ClientDisconnectedHandler { get; set; }
IMqttServerClientSubscribedTopicHandler ClientSubscribedTopicHandler { get; set; }
IMqttServerClientUnsubscribedTopicHandler ClientUnsubscribedTopicHandler { get; set; }
IMqttServerOptions Options { get; }
Task<IList<IMqttClientStatus>> GetClientStatusAsync();
+3 -2
View File
@@ -15,14 +15,15 @@ namespace MQTTnet.Server
IMqttServerConnectionValidator ConnectionValidator { get; }
IMqttServerSubscriptionInterceptor SubscriptionInterceptor { get; }
IMqttServerUnsubscriptionInterceptor UnsubscriptionInterceptor { get; }
IMqttServerApplicationMessageInterceptor ApplicationMessageInterceptor { get; }
IMqttServerClientMessageQueueInterceptor ClientMessageQueueInterceptor { get; }
MqttServerTcpEndpointOptions DefaultEndpointOptions { get; }
MqttServerTlsTcpEndpointOptions TlsEndpointOptions { get; }
IMqttServerStorage Storage { get; }
IMqttServerStorage Storage { get; }
IMqttRetainedMessagesManager RetainedMessagesManager { get; }
}
}
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace MQTTnet.Server
{
public interface IMqttServerUnsubscriptionInterceptor
{
Task InterceptUnsubscriptionAsync(MqttUnsubscriptionInterceptorContext context);
}
}
+86 -68
View File
@@ -1,46 +1,51 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Internal;
using MQTTnet.PacketDispatcher;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using MQTTnet.Server.Status;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Server
{
public class MqttClientConnection : IDisposable
public sealed class MqttClientConnection : IDisposable
{
private readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
private readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
private readonly CancellationTokenSource _cancellationToken = new CancellationTokenSource();
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
readonly CancellationTokenSource _cancellationToken = new CancellationTokenSource();
private readonly MqttRetainedMessagesManager _retainedMessagesManager;
private readonly MqttClientKeepAliveMonitor _keepAliveMonitor;
private readonly MqttClientSessionsManager _sessionsManager;
readonly IMqttRetainedMessagesManager _retainedMessagesManager;
readonly Func<Task> _onStart;
readonly Func<MqttClientDisconnectType, Task> _onStop;
readonly MqttClientKeepAliveMonitor _keepAliveMonitor;
readonly MqttClientSessionsManager _sessionsManager;
private readonly IMqttNetChildLogger _logger;
private readonly IMqttServerOptions _serverOptions;
readonly IMqttNetLogger _logger;
readonly IMqttServerOptions _serverOptions;
private readonly IMqttChannelAdapter _channelAdapter;
private readonly IMqttDataConverter _dataConverter;
private readonly string _endpoint;
private readonly DateTime _connectedTimestamp;
readonly IMqttChannelAdapter _channelAdapter;
readonly IMqttDataConverter _dataConverter;
readonly string _endpoint;
readonly DateTime _connectedTimestamp;
private Task<MqttClientDisconnectType> _packageReceiverTask;
private DateTime _lastPacketReceivedTimestamp;
private DateTime _lastNonKeepAlivePacketReceivedTimestamp;
private long _receivedPacketsCount;
private long _sentPacketsCount = 1; // Start with 1 because the CONNECT packet is not counted anywhere.
private long _receivedApplicationMessagesCount;
private long _sentApplicationMessagesCount;
volatile Task _packageReceiverTask;
DateTime _lastPacketReceivedTimestamp;
DateTime _lastNonKeepAlivePacketReceivedTimestamp;
long _receivedPacketsCount;
long _sentPacketsCount = 1; // Start with 1 because the CONNECT packet is not counted anywhere.
long _receivedApplicationMessagesCount;
long _sentApplicationMessagesCount;
volatile bool _isTakeover;
public MqttClientConnection(
MqttConnectPacket connectPacket,
@@ -48,14 +53,18 @@ namespace MQTTnet.Server
MqttClientSession session,
IMqttServerOptions serverOptions,
MqttClientSessionsManager sessionsManager,
MqttRetainedMessagesManager retainedMessagesManager,
IMqttNetChildLogger logger)
IMqttRetainedMessagesManager retainedMessagesManager,
Func<Task> onStart,
Func<MqttClientDisconnectType, Task> onStop,
IMqttNetLogger logger)
{
Session = session ?? throw new ArgumentNullException(nameof(session));
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
_retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager));
_onStart = onStart ?? throw new ArgumentNullException(nameof(onStart));
_onStop = onStop ?? throw new ArgumentNullException(nameof(onStop));
_channelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter));
_dataConverter = _channelAdapter.PacketFormatterAdapter.DataConverter;
_endpoint = _channelAdapter.Endpoint;
@@ -64,7 +73,7 @@ namespace MQTTnet.Server
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateChildLogger(nameof(MqttClientConnection));
_keepAliveMonitor = new MqttClientKeepAliveMonitor(ConnectPacket.ClientId, StopAsync, _logger);
_keepAliveMonitor = new MqttClientKeepAliveMonitor(ConnectPacket.ClientId, () => StopAsync(), _logger);
_connectedTimestamp = DateTime.UtcNow;
_lastPacketReceivedTimestamp = _connectedTimestamp;
@@ -76,16 +85,20 @@ namespace MQTTnet.Server
public string ClientId => ConnectPacket.ClientId;
public MqttClientSession Session { get; }
public async Task StopAsync()
public Task StopAsync(bool isTakeover = false)
{
_isTakeover = isTakeover;
var task = _packageReceiverTask;
StopInternal();
var task = _packageReceiverTask;
if (task != null)
{
await task.ConfigureAwait(false);
return task;
}
return PlatformAbstractionLayer.CompletedTask;
}
public void ResetStatistics()
@@ -112,25 +125,26 @@ namespace MQTTnet.Server
status.BytesSent = _channelAdapter.BytesSent;
status.BytesReceived = _channelAdapter.BytesReceived;
}
public void Dispose()
{
_cancellationToken.Dispose();
}
public Task<MqttClientDisconnectType> RunAsync()
public Task RunAsync(MqttConnectionValidatorContext connectionValidatorContext)
{
_packageReceiverTask = RunInternalAsync();
_packageReceiverTask = RunInternalAsync(connectionValidatorContext);
return _packageReceiverTask;
}
private async Task<MqttClientDisconnectType> RunInternalAsync()
async Task RunInternalAsync(MqttConnectionValidatorContext connectionValidatorContext)
{
var disconnectType = MqttClientDisconnectType.NotClean;
try
{
await _onStart();
_logger.Info("Client '{0}': Session started.", ClientId);
_channelAdapter.ReadingPacketStartedCallback = OnAdapterReadingPacketStarted;
_channelAdapter.ReadingPacketCompletedCallback = OnAdapterReadingPacketCompleted;
@@ -142,12 +156,8 @@ namespace MQTTnet.Server
_keepAliveMonitor.Start(ConnectPacket.KeepAlivePeriod, _cancellationToken.Token);
await SendAsync(
new MqttConnAckPacket
{
ReturnCode = MqttConnectReturnCode.ConnectionAccepted,
ReasonCode = MqttConnectReasonCode.Success,
IsSessionPresent = !Session.IsCleanSession
}).ConfigureAwait(false);
_channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(connectionValidatorContext)
).ConfigureAwait(false);
Session.IsCleanSession = false;
@@ -236,6 +246,11 @@ namespace MQTTnet.Server
}
finally
{
if (_isTakeover)
{
disconnectType = MqttClientDisconnectType.Takeover;
}
if (Session.WillMessage != null)
{
_sessionsManager.DispatchApplicationMessage(Session.WillMessage, this);
@@ -247,20 +262,27 @@ namespace MQTTnet.Server
_channelAdapter.ReadingPacketStartedCallback = null;
_channelAdapter.ReadingPacketCompletedCallback = null;
_logger.Info("Client '{0}': Session stopped.", ClientId);
_packageReceiverTask = null;
}
_logger.Info("Client '{0}': Connection stopped.", ClientId);
return disconnectType;
_packageReceiverTask = null;
try
{
await _onStop(disconnectType);
}
catch (Exception e)
{
_logger.Error(e, "client '{0}': Error while cleaning up", ClientId);
}
}
}
private void StopInternal()
void StopInternal()
{
_cancellationToken.Cancel(false);
}
private async Task EnqueueSubscribedRetainedMessagesAsync(ICollection<TopicFilter> topicFilters)
async Task EnqueueSubscribedRetainedMessagesAsync(ICollection<TopicFilter> topicFilters)
{
var retainedMessages = await _retainedMessagesManager.GetSubscribedMessagesAsync(topicFilters).ConfigureAwait(false);
foreach (var applicationMessage in retainedMessages)
@@ -269,7 +291,7 @@ namespace MQTTnet.Server
}
}
private async Task HandleIncomingSubscribePacketAsync(MqttSubscribePacket subscribePacket)
async Task HandleIncomingSubscribePacketAsync(MqttSubscribePacket subscribePacket)
{
// TODO: Let the channel adapter create the packet.
var subscribeResult = await Session.SubscriptionsManager.SubscribeAsync(subscribePacket, ConnectPacket).ConfigureAwait(false);
@@ -285,14 +307,14 @@ namespace MQTTnet.Server
await EnqueueSubscribedRetainedMessagesAsync(subscribePacket.TopicFilters).ConfigureAwait(false);
}
private async Task HandleIncomingUnsubscribePacketAsync(MqttUnsubscribePacket unsubscribePacket)
async Task HandleIncomingUnsubscribePacketAsync(MqttUnsubscribePacket unsubscribePacket)
{
// TODO: Let the channel adapter create the packet.
var unsubscribeResult = await Session.SubscriptionsManager.UnsubscribeAsync(unsubscribePacket).ConfigureAwait(false);
await SendAsync(unsubscribeResult).ConfigureAwait(false);
}
private Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
{
Interlocked.Increment(ref _sentApplicationMessagesCount);
@@ -317,16 +339,16 @@ namespace MQTTnet.Server
}
}
private Task HandleIncomingPublishPacketWithQoS0Async(MqttPublishPacket publishPacket)
Task HandleIncomingPublishPacketWithQoS0Async(MqttPublishPacket publishPacket)
{
var applicationMessage = _dataConverter.CreateApplicationMessage(publishPacket);
_sessionsManager.DispatchApplicationMessage(applicationMessage, this);
return Task.FromResult(0);
return PlatformAbstractionLayer.CompletedTask;
}
private Task HandleIncomingPublishPacketWithQoS1Async(MqttPublishPacket publishPacket)
Task HandleIncomingPublishPacketWithQoS1Async(MqttPublishPacket publishPacket)
{
var applicationMessage = _dataConverter.CreateApplicationMessage(publishPacket);
_sessionsManager.DispatchApplicationMessage(applicationMessage, this);
@@ -335,7 +357,7 @@ namespace MQTTnet.Server
return SendAsync(pubAckPacket);
}
private Task HandleIncomingPublishPacketWithQoS2Async(MqttPublishPacket publishPacket)
Task HandleIncomingPublishPacketWithQoS2Async(MqttPublishPacket publishPacket)
{
var applicationMessage = _dataConverter.CreateApplicationMessage(publishPacket);
_sessionsManager.DispatchApplicationMessage(applicationMessage, this);
@@ -349,7 +371,7 @@ namespace MQTTnet.Server
return SendAsync(pubRecPacket);
}
private async Task SendPendingPacketsAsync(CancellationToken cancellationToken)
async Task SendPendingPacketsAsync(CancellationToken cancellationToken)
{
MqttQueuedApplicationMessage queuedApplicationMessage = null;
MqttPublishPacket publishPacket = null;
@@ -426,9 +448,6 @@ namespace MQTTnet.Server
}
_logger.Verbose("Queued application message sent (ClientId: {0}).", ClientId);
// TODO:
//Interlocked.Increment(ref _sentPacketsCount);
}
}
catch (Exception exception)
@@ -441,9 +460,8 @@ namespace MQTTnet.Server
{
_logger.Warning(exception, "Sending publish packet failed: Communication exception (ClientId: {0}).", ClientId);
}
else if (exception is OperationCanceledException && _cancellationToken.Token.IsCancellationRequested)
else if (exception is OperationCanceledException)
{
// The cancellation was triggered externally.
}
else
{
@@ -464,7 +482,7 @@ namespace MQTTnet.Server
}
}
private async Task SendAsync(MqttBasePacket packet)
async Task SendAsync(MqttBasePacket packet)
{
await _channelAdapter.SendPacketAsync(packet, _serverOptions.DefaultCommunicationTimeout, _cancellationToken.Token).ConfigureAwait(false);
@@ -476,12 +494,12 @@ namespace MQTTnet.Server
}
}
private void OnAdapterReadingPacketCompleted()
void OnAdapterReadingPacketCompleted()
{
_keepAliveMonitor?.Resume();
}
private void OnAdapterReadingPacketStarted()
void OnAdapterReadingPacketStarted()
{
_keepAliveMonitor?.Pause();
}
@@ -1,27 +1,27 @@
using System;
using MQTTnet.Diagnostics;
using MQTTnet.Internal;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
using MQTTnet.Internal;
namespace MQTTnet.Server
{
public class MqttClientKeepAliveMonitor
{
private readonly Stopwatch _lastPacketReceivedTracker = new Stopwatch();
readonly Stopwatch _lastPacketReceivedTracker = new Stopwatch();
private readonly string _clientId;
private readonly Func<Task> _keepAliveElapsedCallback;
private readonly IMqttNetChildLogger _logger;
readonly string _clientId;
readonly Func<Task> _keepAliveElapsedCallback;
readonly IMqttNetLogger _logger;
private bool _isPaused;
bool _isPaused;
public MqttClientKeepAliveMonitor(string clientId, Func<Task> keepAliveElapsedCallback, IMqttNetChildLogger logger)
public MqttClientKeepAliveMonitor(string clientId, Func<Task> keepAliveElapsedCallback, IMqttNetLogger logger)
{
_clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
_keepAliveElapsedCallback = keepAliveElapsedCallback ?? throw new ArgumentNullException(nameof(keepAliveElapsedCallback));
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateChildLogger(nameof(MqttClientKeepAliveMonitor));
}
@@ -32,7 +32,7 @@ namespace MQTTnet.Server
{
return;
}
Task.Run(() => RunAsync(keepAlivePeriod, cancellationToken), cancellationToken).Forget(_logger);
}
@@ -51,7 +51,7 @@ namespace MQTTnet.Server
_lastPacketReceivedTracker.Restart();
}
private async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken)
async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken)
{
try
{
+15 -9
View File
@@ -1,22 +1,23 @@
using System;
using MQTTnet.Diagnostics;
using MQTTnet.Server.Status;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
using MQTTnet.Server.Status;
namespace MQTTnet.Server
{
public class MqttClientSession
{
private readonly IMqttNetChildLogger _logger;
readonly IMqttNetLogger _logger;
private readonly DateTime _createdTimestamp = DateTime.UtcNow;
readonly DateTime _createdTimestamp = DateTime.UtcNow;
readonly IMqttRetainedMessagesManager _retainedMessagesManager;
public MqttClientSession(string clientId, IDictionary<object, object> items, MqttServerEventDispatcher eventDispatcher, IMqttServerOptions serverOptions, IMqttNetChildLogger logger)
public MqttClientSession(string clientId, IDictionary<object, object> items, MqttServerEventDispatcher eventDispatcher, IMqttServerOptions serverOptions, IMqttRetainedMessagesManager retainedMessagesManager, IMqttNetLogger logger)
{
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
Items = items ?? throw new ArgumentNullException(nameof(items));
_retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager));
SubscriptionsManager = new MqttClientSubscriptionsManager(this, eventDispatcher, serverOptions);
ApplicationMessagesQueue = new MqttClientSessionApplicationMessagesQueue(serverOptions);
@@ -52,11 +53,13 @@ namespace MQTTnet.Server
ApplicationMessagesQueue.Enqueue(applicationMessage, senderClientId, checkSubscriptionsResult.QualityOfServiceLevel, isRetainedApplicationMessage);
}
public async Task SubscribeAsync(ICollection<TopicFilter> topicFilters, MqttRetainedMessagesManager retainedMessagesManager)
public async Task SubscribeAsync(ICollection<TopicFilter> topicFilters)
{
if (topicFilters is null) throw new ArgumentNullException(nameof(topicFilters));
await SubscriptionsManager.SubscribeAsync(topicFilters).ConfigureAwait(false);
var matchingRetainedMessages = await retainedMessagesManager.GetSubscribedMessagesAsync(topicFilters).ConfigureAwait(false);
var matchingRetainedMessages = await _retainedMessagesManager.GetSubscribedMessagesAsync(topicFilters).ConfigureAwait(false);
foreach (var matchingRetainedMessage in matchingRetainedMessages)
{
EnqueueApplicationMessage(matchingRetainedMessage, null, true);
@@ -65,6 +68,8 @@ namespace MQTTnet.Server
public Task UnsubscribeAsync(IEnumerable<string> topicFilters)
{
if (topicFilters is null) throw new ArgumentNullException(nameof(topicFilters));
return SubscriptionsManager.UnsubscribeAsync(topicFilters);
}
@@ -73,6 +78,7 @@ namespace MQTTnet.Server
status.ClientId = ClientId;
status.CreatedTimestamp = _createdTimestamp;
status.PendingApplicationMessagesCount = ApplicationMessagesQueue.Count;
status.Items = Items;
}
}
}
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace MQTTnet.Server
{
public class MqttClientSessionApplicationMessagesQueue : IDisposable
public class MqttClientSessionApplicationMessagesQueue : Disposable
{
private readonly AsyncQueue<MqttQueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttQueuedApplicationMessage>();
@@ -71,9 +71,14 @@ namespace MQTTnet.Server
}
}
public void Dispose()
protected override void Dispose(bool disposing)
{
_messageQueue.Dispose();
if (disposing)
{
_messageQueue.Dispose();
}
base.Dispose(disposing);
}
}
}
@@ -1,40 +1,41 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Formatter;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using MQTTnet.Server.Status;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Server
{
public class MqttClientSessionsManager : IDisposable
public class MqttClientSessionsManager : Disposable
{
private readonly AsyncQueue<MqttEnqueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttEnqueuedApplicationMessage>();
readonly AsyncQueue<MqttEnqueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttEnqueuedApplicationMessage>();
private readonly SemaphoreSlim _createConnectionGate = new SemaphoreSlim(1, 1);
private readonly ConcurrentDictionary<string, MqttClientConnection> _connections = new ConcurrentDictionary<string, MqttClientConnection>();
private readonly ConcurrentDictionary<string, MqttClientSession> _sessions = new ConcurrentDictionary<string, MqttClientSession>();
private readonly IDictionary<object, object> _serverSessionItems = new ConcurrentDictionary<object, object>();
readonly AsyncLock _createConnectionGate = new AsyncLock();
readonly ConcurrentDictionary<string, MqttClientConnection> _connections = new ConcurrentDictionary<string, MqttClientConnection>();
readonly ConcurrentDictionary<string, MqttClientSession> _sessions = new ConcurrentDictionary<string, MqttClientSession>();
readonly IDictionary<object, object> _serverSessionItems = new ConcurrentDictionary<object, object>();
private readonly CancellationToken _cancellationToken;
private readonly MqttServerEventDispatcher _eventDispatcher;
readonly CancellationToken _cancellationToken;
readonly MqttServerEventDispatcher _eventDispatcher;
private readonly MqttRetainedMessagesManager _retainedMessagesManager;
private readonly IMqttServerOptions _options;
private readonly IMqttNetChildLogger _logger;
readonly IMqttRetainedMessagesManager _retainedMessagesManager;
readonly IMqttServerOptions _options;
readonly IMqttNetLogger _logger;
public MqttClientSessionsManager(
IMqttServerOptions options,
MqttRetainedMessagesManager retainedMessagesManager,
IMqttRetainedMessagesManager retainedMessagesManager,
CancellationToken cancellationToken,
MqttServerEventDispatcher eventDispatcher,
IMqttNetChildLogger logger)
IMqttNetLogger logger)
{
_cancellationToken = cancellationToken;
@@ -59,9 +60,11 @@ namespace MQTTnet.Server
}
}
public Task HandleClientAsync(IMqttChannelAdapter clientAdapter)
public Task HandleClientConnectionAsync(IMqttChannelAdapter clientAdapter)
{
return HandleClientAsync(clientAdapter, _cancellationToken);
if (clientAdapter is null) throw new ArgumentNullException(nameof(clientAdapter));
return HandleClientConnectionAsync(clientAdapter, _cancellationToken);
}
public Task<IList<IMqttClientStatus>> GetClientStatusAsync()
@@ -72,7 +75,7 @@ namespace MQTTnet.Server
{
var clientStatus = new MqttClientStatus(connection);
connection.FillStatus(clientStatus);
var sessionStatus = new MqttSessionStatus(connection.Session, this);
connection.Session.FillStatus(sessionStatus);
clientStatus.Session = sessionStatus;
@@ -91,7 +94,7 @@ namespace MQTTnet.Server
{
var sessionStatus = new MqttSessionStatus(session, this);
session.FillStatus(sessionStatus);
result.Add(sessionStatus);
}
@@ -115,7 +118,7 @@ namespace MQTTnet.Server
throw new InvalidOperationException($"Client session '{clientId}' is unknown.");
}
return session.SubscribeAsync(topicFilters, _retainedMessagesManager);
return session.SubscribeAsync(topicFilters);
}
public Task UnsubscribeAsync(string clientId, IEnumerable<string> topicFilters)
@@ -145,12 +148,16 @@ namespace MQTTnet.Server
_logger.Verbose("Session for client '{0}' deleted.", clientId);
}
public void Dispose()
protected override void Dispose(bool disposing)
{
_messageQueue?.Dispose();
if (disposing)
{
_messageQueue?.Dispose();
}
base.Dispose(disposing);
}
private async Task TryProcessQueuedApplicationMessagesAsync(CancellationToken cancellationToken)
async Task TryProcessQueuedApplicationMessagesAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
@@ -168,7 +175,7 @@ namespace MQTTnet.Server
}
}
private async Task TryProcessNextQueuedApplicationMessageAsync(CancellationToken cancellationToken)
async Task TryProcessNextQueuedApplicationMessageAsync(CancellationToken cancellationToken)
{
try
{
@@ -178,6 +185,12 @@ namespace MQTTnet.Server
}
var dequeueResult = await _messageQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);
if (!dequeueResult.IsSuccess)
{
return;
}
var queuedApplicationMessage = dequeueResult.Item;
var sender = queuedApplicationMessage.Sender;
@@ -202,7 +215,7 @@ namespace MQTTnet.Server
applicationMessage = interceptorContext.ApplicationMessage;
}
await _eventDispatcher.HandleApplicationMessageReceivedAsync(sender?.ClientId, applicationMessage).ConfigureAwait(false);
await _eventDispatcher.SafeNotifyApplicationMessageReceivedAsync(sender?.ClientId, applicationMessage).ConfigureAwait(false);
if (applicationMessage.Retain)
{
@@ -226,22 +239,29 @@ namespace MQTTnet.Server
}
}
private async Task HandleClientAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
{
var disconnectType = MqttClientDisconnectType.NotClean;
string clientId = null;
MqttConnectPacket connectPacket;
try
{
var firstPacket = await channelAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false);
if (!(firstPacket is MqttConnectPacket connectPacket))
try
{
_logger.Warning(null, "The first packet from client '{0}' was no 'CONNECT' packet [MQTT-3.1.0-1].", channelAdapter.Endpoint);
var firstPacket = await channelAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false);
connectPacket = firstPacket as MqttConnectPacket;
if (connectPacket == null)
{
_logger.Warning(null, "The first packet from client '{0}' was no 'CONNECT' packet [MQTT-3.1.0-1].", channelAdapter.Endpoint);
return;
}
}
catch (MqttCommunicationTimedOutException)
{
_logger.Warning(null, "Client '{0}' connected but did not sent a CONNECT packet.", channelAdapter.Endpoint);
return;
}
clientId = connectPacket.ClientId;
var connectionValidatorContext = await ValidateConnectionAsync(connectPacket, channelAdapter).ConfigureAwait(false);
if (connectionValidatorContext.ReasonCode != MqttConnectReasonCode.Success)
@@ -254,11 +274,17 @@ namespace MQTTnet.Server
return;
}
var connection = await CreateConnectionAsync(connectPacket, connectionValidatorContext, channelAdapter).ConfigureAwait(false);
clientId = connectPacket.ClientId;
await _eventDispatcher.HandleClientConnectedAsync(clientId).ConfigureAwait(false);
disconnectType = await connection.RunAsync().ConfigureAwait(false);
var connection = await CreateClientConnectionAsync(
connectPacket,
connectionValidatorContext,
channelAdapter,
async () => await _eventDispatcher.SafeNotifyClientConnectedAsync(clientId).ConfigureAwait(false),
async disconnectType => await CleanUpClient(clientId, channelAdapter, disconnectType)
).ConfigureAwait(false);
await connection.RunAsync(connectionValidatorContext).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -267,28 +293,29 @@ namespace MQTTnet.Server
{
_logger.Error(exception, exception.Message);
}
finally
}
private async Task CleanUpClient(string clientId, IMqttChannelAdapter channelAdapter, MqttClientDisconnectType disconnectType)
{
if (clientId != null)
{
if (clientId != null)
_connections.TryRemove(clientId, out _);
if (!_options.EnablePersistentSessions)
{
_connections.TryRemove(clientId, out _);
if (!_options.EnablePersistentSessions)
{
await DeleteSessionAsync(clientId).ConfigureAwait(false);
}
await DeleteSessionAsync(clientId).ConfigureAwait(false);
}
}
await TryCleanupChannelAsync(channelAdapter).ConfigureAwait(false);
await SafeCleanupChannelAsync(channelAdapter).ConfigureAwait(false);
if (clientId != null)
{
await _eventDispatcher.TryHandleClientDisconnectedAsync(clientId, disconnectType).ConfigureAwait(false);
}
if (clientId != null)
{
await _eventDispatcher.SafeNotifyClientDisconnectedAsync(clientId, disconnectType).ConfigureAwait(false);
}
}
private async Task<MqttConnectionValidatorContext> ValidateConnectionAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter)
async Task<MqttConnectionValidatorContext> ValidateConnectionAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter)
{
var context = new MqttConnectionValidatorContext(connectPacket, channelAdapter, new ConcurrentDictionary<object, object>());
@@ -316,25 +343,24 @@ namespace MQTTnet.Server
return context;
}
private async Task<MqttClientConnection> CreateConnectionAsync(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter)
async Task<MqttClientConnection> CreateClientConnectionAsync(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter, Func<Task> onStart, Func<MqttClientDisconnectType, Task> onStop)
{
await _createConnectionGate.WaitAsync(_cancellationToken).ConfigureAwait(false);
try
using (await _createConnectionGate.WaitAsync(_cancellationToken).ConfigureAwait(false))
{
var isSessionPresent = _sessions.TryGetValue(connectPacket.ClientId, out var session);
var isConnectionPresent = _connections.TryGetValue(connectPacket.ClientId, out var existingConnection);
if (isConnectionPresent)
{
await existingConnection.StopAsync().ConfigureAwait(false);
await existingConnection.StopAsync(true).ConfigureAwait(false);
}
if (isSessionPresent)
{
if (connectPacket.CleanSession)
{
session = null;
_logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId);
}
else
@@ -345,24 +371,20 @@ namespace MQTTnet.Server
if (session == null)
{
session = new MqttClientSession(connectPacket.ClientId, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _logger);
session = new MqttClientSession(connectPacket.ClientId, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _retainedMessagesManager, _logger);
_logger.Verbose("Created a new session for client '{0}'.", connectPacket.ClientId);
}
var connection = new MqttClientConnection(connectPacket, channelAdapter, session, _options, this, _retainedMessagesManager, _logger);
var connection = new MqttClientConnection(connectPacket, channelAdapter, session, _options, this, _retainedMessagesManager, onStart, onStop, _logger);
_connections[connection.ClientId] = connection;
_sessions[session.ClientId] = session;
return connection;
}
finally
{
_createConnectionGate.Release();
}
}
private async Task<MqttApplicationMessageInterceptorContext> InterceptApplicationMessageAsync(MqttClientConnection senderConnection, MqttApplicationMessage applicationMessage)
async Task<MqttApplicationMessageInterceptorContext> InterceptApplicationMessageAsync(MqttClientConnection senderConnection, MqttApplicationMessage applicationMessage)
{
var interceptor = _options.ApplicationMessageInterceptor;
if (interceptor == null)
@@ -390,7 +412,7 @@ namespace MQTTnet.Server
return interceptorContext;
}
private async Task TryCleanupChannelAsync(IMqttChannelAdapter channelAdapter)
async Task SafeCleanupChannelAsync(IMqttChannelAdapter channelAdapter)
{
try
{
@@ -1,9 +1,9 @@
using System;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Server
{
@@ -67,7 +67,7 @@ namespace MQTTnet.Server
_subscriptions[finalTopicFilter.Topic] = finalTopicFilter;
}
await _eventDispatcher.HandleClientSubscribedTopicAsync(_clientSession.ClientId, finalTopicFilter).ConfigureAwait(false);
await _eventDispatcher.SafeNotifyClientSubscribedTopicAsync(_clientSession.ClientId, finalTopicFilter).ConfigureAwait(false);
}
}
@@ -83,7 +83,7 @@ namespace MQTTnet.Server
var interceptorContext = await InterceptSubscribeAsync(topicFilter).ConfigureAwait(false);
if (!interceptorContext.AcceptSubscription)
{
continue;
continue;
}
if (interceptorContext.AcceptSubscription)
@@ -93,7 +93,7 @@ namespace MQTTnet.Server
_subscriptions[topicFilter.Topic] = topicFilter;
}
await _eventDispatcher.HandleClientSubscribedTopicAsync(_clientSession.ClientId, topicFilter).ConfigureAwait(false);
await _eventDispatcher.SafeNotifyClientSubscribedTopicAsync(_clientSession.ClientId, topicFilter).ConfigureAwait(false);
}
}
}
@@ -107,9 +107,16 @@ namespace MQTTnet.Server
PacketIdentifier = unsubscribePacket.PacketIdentifier
};
lock (_subscriptions)
foreach (var topicFilter in unsubscribePacket.TopicFilters)
{
foreach (var topicFilter in unsubscribePacket.TopicFilters)
var interceptorContext = await InterceptUnsubscribeAsync(topicFilter).ConfigureAwait(false);
if (!interceptorContext.AcceptUnsubscription)
{
unsubAckPacket.ReasonCodes.Add(MqttUnsubscribeReasonCode.ImplementationSpecificError);
continue;
}
lock (_subscriptions)
{
if (_subscriptions.Remove(topicFilter))
{
@@ -124,25 +131,29 @@ namespace MQTTnet.Server
foreach (var topicFilter in unsubscribePacket.TopicFilters)
{
await _eventDispatcher.HandleClientUnsubscribedTopicAsync(_clientSession.ClientId, topicFilter).ConfigureAwait(false);
await _eventDispatcher.SafeNotifyClientUnsubscribedTopicAsync(_clientSession.ClientId, topicFilter).ConfigureAwait(false);
}
return unsubAckPacket;
}
public Task UnsubscribeAsync(IEnumerable<string> topicFilters)
public async Task UnsubscribeAsync(IEnumerable<string> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
lock (_subscriptions)
foreach (var topicFilter in topicFilters)
{
foreach (var topicFilter in topicFilters)
var interceptorContext = await InterceptUnsubscribeAsync(topicFilter).ConfigureAwait(false);
if (!interceptorContext.AcceptUnsubscription)
{
continue;
}
lock (_subscriptions)
{
_subscriptions.Remove(topicFilter);
}
}
return Task.FromResult(0);
}
public CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel qosLevel)
@@ -206,6 +217,17 @@ namespace MQTTnet.Server
return context;
}
private async Task<MqttUnsubscriptionInterceptorContext> InterceptUnsubscribeAsync(string topicFilter)
{
var context = new MqttUnsubscriptionInterceptorContext(_clientSession.ClientId, topicFilter, _clientSession.Items);
if (_serverOptions.UnsubscriptionInterceptor != null)
{
await _serverOptions.UnsubscriptionInterceptor.InterceptUnsubscriptionAsync(context).ConfigureAwait(false);
}
return context;
}
private static CheckSubscriptionsResult CreateSubscriptionResult(MqttQualityOfServiceLevel qosLevel, HashSet<MqttQualityOfServiceLevel> subscribedQoSLevels)
{
MqttQualityOfServiceLevel effectiveQoS;
@@ -1,26 +1,28 @@
using System;
using MQTTnet.Diagnostics;
using MQTTnet.Implementations;
using MQTTnet.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MQTTnet.Diagnostics;
using MQTTnet.Internal;
namespace MQTTnet.Server
{
public class MqttRetainedMessagesManager
public class MqttRetainedMessagesManager : IMqttRetainedMessagesManager
{
private readonly byte[] _emptyArray = new byte[0];
private readonly AsyncLock _messagesLock = new AsyncLock();
private readonly Dictionary<string, MqttApplicationMessage> _messages = new Dictionary<string, MqttApplicationMessage>();
private readonly IMqttNetChildLogger _logger;
private readonly IMqttServerOptions _options;
private IMqttNetLogger _logger;
private IMqttServerOptions _options;
public MqttRetainedMessagesManager(IMqttServerOptions options, IMqttNetChildLogger logger)
public Task Start(IMqttServerOptions options, IMqttNetLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateChildLogger(nameof(MqttRetainedMessagesManager));
_options = options ?? throw new ArgumentNullException(nameof(options));
return PlatformAbstractionLayer.CompletedTask;
}
public async Task LoadMessagesAsync()
@@ -103,7 +105,7 @@ namespace MQTTnet.Server
}
}
public async Task<List<MqttApplicationMessage>> GetSubscribedMessagesAsync(ICollection<TopicFilter> topicFilters)
public async Task<IList<MqttApplicationMessage>> GetSubscribedMessagesAsync(ICollection<TopicFilter> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
@@ -128,7 +130,7 @@ namespace MQTTnet.Server
break;
}
}
return matchingRetainedMessages;
}
+27 -21
View File
@@ -1,28 +1,29 @@
using System;
using MQTTnet.Adapter;
using MQTTnet.Client.Publishing;
using MQTTnet.Client.Receiving;
using MQTTnet.Diagnostics;
using MQTTnet.Exceptions;
using MQTTnet.Protocol;
using MQTTnet.Server.Status;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Client.Publishing;
using MQTTnet.Client.Receiving;
using MQTTnet.Diagnostics;
using MQTTnet.Protocol;
using MQTTnet.Server.Status;
namespace MQTTnet.Server
{
public class MqttServer : IMqttServer
{
private readonly MqttServerEventDispatcher _eventDispatcher;
private readonly ICollection<IMqttServerAdapter> _adapters;
private readonly IMqttNetChildLogger _logger;
readonly MqttServerEventDispatcher _eventDispatcher;
readonly ICollection<IMqttServerAdapter> _adapters;
readonly IMqttNetLogger _logger;
private MqttClientSessionsManager _clientSessionsManager;
private MqttRetainedMessagesManager _retainedMessagesManager;
private CancellationTokenSource _cancellationTokenSource;
MqttClientSessionsManager _clientSessionsManager;
IMqttRetainedMessagesManager _retainedMessagesManager;
CancellationTokenSource _cancellationTokenSource;
public MqttServer(IEnumerable<IMqttServerAdapter> adapters, IMqttNetChildLogger logger)
public MqttServer(IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
{
if (adapters == null) throw new ArgumentNullException(nameof(adapters));
_adapters = adapters.ToList();
@@ -33,6 +34,8 @@ namespace MQTTnet.Server
_eventDispatcher = new MqttServerEventDispatcher(logger.CreateChildLogger(nameof(MqttServerEventDispatcher)));
}
public bool IsStarted => _cancellationTokenSource != null;
public IMqttServerStartedHandler StartedHandler { get; set; }
public IMqttServerStoppedHandler StoppedHandler { get; set; }
@@ -48,7 +51,7 @@ namespace MQTTnet.Server
get => _eventDispatcher.ClientDisconnectedHandler;
set => _eventDispatcher.ClientDisconnectedHandler = value;
}
public IMqttServerClientSubscribedTopicHandler ClientSubscribedTopicHandler
{
get => _eventDispatcher.ClientSubscribedTopicHandler;
@@ -60,7 +63,7 @@ namespace MQTTnet.Server
get => _eventDispatcher.ClientUnsubscribedTopicHandler;
set => _eventDispatcher.ClientUnsubscribedTopicHandler = value;
}
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler
{
get => _eventDispatcher.ApplicationMessageReceivedHandler;
@@ -117,11 +120,14 @@ namespace MQTTnet.Server
{
Options = options ?? throw new ArgumentNullException(nameof(options));
if (Options.RetainedMessagesManager == null) throw new MqttConfigurationException("options.RetainedMessagesManager should not be null.");
if (_cancellationTokenSource != null) throw new InvalidOperationException("The server is already started.");
_cancellationTokenSource = new CancellationTokenSource();
_retainedMessagesManager = new MqttRetainedMessagesManager(Options, _logger);
_retainedMessagesManager = Options.RetainedMessagesManager;
await _retainedMessagesManager.Start(Options, _logger);
await _retainedMessagesManager.LoadMessagesAsync().ConfigureAwait(false);
_clientSessionsManager = new MqttClientSessionsManager(Options, _retainedMessagesManager, _cancellationTokenSource.Token, _eventDispatcher, _logger);
@@ -150,9 +156,9 @@ namespace MQTTnet.Server
{
return;
}
await _clientSessionsManager.StopAsync().ConfigureAwait(false);
_cancellationTokenSource.Cancel(false);
foreach (var adapter in _adapters)
@@ -186,9 +192,9 @@ namespace MQTTnet.Server
return _retainedMessagesManager?.ClearMessagesAsync();
}
private Task OnHandleClient(IMqttChannelAdapter channelAdapter)
Task OnHandleClient(IMqttChannelAdapter channelAdapter)
{
return _clientSessionsManager.HandleClientAsync(channelAdapter);
return _clientSessionsManager.HandleClientConnectionAsync(channelAdapter);
}
}
}
@@ -1,15 +1,15 @@
using System;
using System.Threading.Tasks;
using MQTTnet.Client.Receiving;
using MQTTnet.Client.Receiving;
using MQTTnet.Diagnostics;
using System;
using System.Threading.Tasks;
namespace MQTTnet.Server
{
public class MqttServerEventDispatcher
{
private readonly IMqttNetChildLogger _logger;
readonly IMqttNetLogger _logger;
public MqttServerEventDispatcher(IMqttNetChildLogger logger)
public MqttServerEventDispatcher(IMqttNetLogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
@@ -24,18 +24,25 @@ namespace MQTTnet.Server
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; }
public Task HandleClientConnectedAsync(string clientId)
public async Task SafeNotifyClientConnectedAsync(string clientId)
{
var handler = ClientConnectedHandler;
if (handler == null)
try
{
return Task.FromResult(0);
}
var handler = ClientConnectedHandler;
if (handler == null)
{
return;
}
return handler.HandleClientConnectedAsync(new MqttServerClientConnectedEventArgs(clientId));
await handler.HandleClientConnectedAsync(new MqttServerClientConnectedEventArgs(clientId)).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error(exception, "Error while handling custom 'ClientConnected' event.");
}
}
public async Task TryHandleClientDisconnectedAsync(string clientId, MqttClientDisconnectType disconnectType)
public async Task SafeNotifyClientDisconnectedAsync(string clientId, MqttClientDisconnectType disconnectType)
{
try
{
@@ -49,41 +56,62 @@ namespace MQTTnet.Server
}
catch (Exception exception)
{
_logger.Error(exception, "Error while handling 'ClientDisconnected' event.");
_logger.Error(exception, "Error while handling custom 'ClientDisconnected' event.");
}
}
public Task HandleClientSubscribedTopicAsync(string clientId, TopicFilter topicFilter)
public async Task SafeNotifyClientSubscribedTopicAsync(string clientId, TopicFilter topicFilter)
{
var handler = ClientSubscribedTopicHandler;
if (handler == null)
try
{
return Task.FromResult(0);
}
var handler = ClientSubscribedTopicHandler;
if (handler == null)
{
return;
}
return handler.HandleClientSubscribedTopicAsync(new MqttServerClientSubscribedTopicEventArgs(clientId, topicFilter));
await handler.HandleClientSubscribedTopicAsync(new MqttServerClientSubscribedTopicEventArgs(clientId, topicFilter)).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error(exception, "Error while handling custom 'ClientSubscribedTopic' event.");
}
}
public Task HandleClientUnsubscribedTopicAsync(string clientId, string topicFilter)
public async Task SafeNotifyClientUnsubscribedTopicAsync(string clientId, string topicFilter)
{
var handler = ClientUnsubscribedTopicHandler;
if (handler == null)
try
{
return Task.FromResult(0);
}
var handler = ClientUnsubscribedTopicHandler;
if (handler == null)
{
return;
}
return handler.HandleClientUnsubscribedTopicAsync(new MqttServerClientUnsubscribedTopicEventArgs(clientId, topicFilter));
await handler.HandleClientUnsubscribedTopicAsync(new MqttServerClientUnsubscribedTopicEventArgs(clientId, topicFilter)).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error(exception, "Error while handling custom 'ClientUnsubscribedTopic' event.");
}
}
public Task HandleApplicationMessageReceivedAsync(string senderClientId, MqttApplicationMessage applicationMessage)
public async Task SafeNotifyApplicationMessageReceivedAsync(string senderClientId, MqttApplicationMessage applicationMessage)
{
var handler = ApplicationMessageReceivedHandler;
if (handler == null)
try
{
return Task.FromResult(0);
}
var handler = ApplicationMessageReceivedHandler;
if (handler == null)
{
return;
}
return handler.HandleApplicationMessageReceivedAsync(new MqttApplicationMessageReceivedEventArgs(senderClientId, applicationMessage));
await handler.HandleApplicationMessageReceivedAsync(new MqttApplicationMessageReceivedEventArgs(senderClientId, applicationMessage)).ConfigureAwait(false); ;
}
catch (Exception exception)
{
_logger.Error(exception, "Error while handling custom 'ApplicationMessageReceived' event.");
}
}
}
}
+5 -1
View File
@@ -21,11 +21,15 @@ namespace MQTTnet.Server
public IMqttServerConnectionValidator ConnectionValidator { get; set; }
public IMqttServerApplicationMessageInterceptor ApplicationMessageInterceptor { get; set; }
public IMqttServerClientMessageQueueInterceptor ClientMessageQueueInterceptor { get; set; }
public IMqttServerSubscriptionInterceptor SubscriptionInterceptor { get; set; }
public IMqttServerUnsubscriptionInterceptor UnsubscriptionInterceptor { get; set; }
public IMqttServerStorage Storage { get; set; }
public IMqttRetainedMessagesManager RetainedMessagesManager { get; set; } = new MqttRetainedMessagesManager();
}
}
@@ -57,7 +57,7 @@ namespace MQTTnet.Server
_options.DefaultEndpointOptions.IsEnabled = false;
return this;
}
public MqttServerOptionsBuilder WithEncryptedEndpoint()
{
_options.TlsEndpointOptions.IsEnabled = true;
@@ -118,13 +118,19 @@ namespace MQTTnet.Server
return this;
}
#endif
public MqttServerOptionsBuilder WithStorage(IMqttServerStorage value)
{
_options.Storage = value;
return this;
}
public MqttServerOptionsBuilder WithRetainedMessagesManager(IMqttRetainedMessagesManager value)
{
_options.RetainedMessagesManager = value;
return this;
}
public MqttServerOptionsBuilder WithConnectionValidator(IMqttServerConnectionValidator value)
{
_options.ConnectionValidator = value;
@@ -155,6 +161,12 @@ namespace MQTTnet.Server
return this;
}
public MqttServerOptionsBuilder WithUnsubscriptionInterceptor(IMqttServerUnsubscriptionInterceptor value)
{
_options.UnsubscriptionInterceptor = value;
return this;
}
public MqttServerOptionsBuilder WithSubscriptionInterceptor(Action<MqttSubscriptionInterceptorContext> value)
{
_options.SubscriptionInterceptor = new MqttServerSubscriptionInterceptorDelegate(value);
@@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace MQTTnet.Server
{
public class MqttUnsubscriptionInterceptorContext
{
public MqttUnsubscriptionInterceptorContext(string clientId, string topic, IDictionary<object, object> sessionItems)
{
ClientId = clientId;
Topic = topic;
SessionItems = sessionItems;
}
public string ClientId { get; }
public string Topic { get; set; }
/// <summary>
/// Gets or sets a key/value collection that can be used to share data within the scope of this session.
/// </summary>
public IDictionary<object, object> SessionItems { get; }
public bool AcceptUnsubscription { get; set; } = true;
public bool CloseConnection { get; set; }
}
}
@@ -1,6 +1,6 @@
using System;
using MQTTnet.Formatter;
using System;
using System.Threading.Tasks;
using MQTTnet.Formatter;
namespace MQTTnet.Server.Status
{
@@ -9,7 +9,7 @@ namespace MQTTnet.Server.Status
string ClientId { get; }
string Endpoint { get; }
MqttProtocolVersion ProtocolVersion { get; }
DateTime LastPacketReceivedTimestamp { get; }
@@ -29,7 +29,7 @@ namespace MQTTnet.Server.Status
long BytesSent { get; }
long BytesReceived { get; }
Task DisconnectAsync();
void ResetStatistics();
@@ -1,12 +1,15 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Server.Status
{
public interface IMqttSessionStatus
{
string ClientId { get; set; }
string ClientId { get; }
long PendingApplicationMessagesCount { get; set; }
long PendingApplicationMessagesCount { get; }
IDictionary<object, object> Items { get; }
Task ClearPendingApplicationMessagesAsync();
@@ -1,16 +1,16 @@
using System;
using MQTTnet.Formatter;
using System;
using System.Threading.Tasks;
using MQTTnet.Formatter;
namespace MQTTnet.Server.Status
{
public class MqttClientStatus : IMqttClientStatus
{
private readonly MqttClientConnection _connection;
readonly MqttClientConnection _connection;
public MqttClientStatus(MqttClientConnection connection)
{
_connection = connection;
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
}
public string ClientId { get; set; }

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