Compare commits

..
149 Commits
Author SHA1 Message Date
HansM bb52159e49 Added reference to SparkplugNet. 2021-01-31 17:35:39 +01:00
HansM 109acccd19 Update MQTTnet.nuspec
Added code documentation hint.
2021-01-31 11:34:27 +01:00
HansM deb2c3dec6 Merge pull request #1055 from chkr1011/add-code-doc
Added client documentation.
2021-01-31 11:33:13 +01:00
HansM 63ca6086d5 Added missing changelog entry.
Missing changelog change from https://github.com/chkr1011/MQTTnet/pull/1076.
2021-01-30 15:39:04 +01:00
Christian 3f92f2ca2b Remove UWP test app because it gets replaced by MQTTnet.App in a separate repository. 2021-01-30 13:24:24 +01:00
Christian a13874d8a5 Add missing overloads for client disconnect.
Add timeout for disconnect in managed client.
2021-01-30 13:17:03 +01:00
Christian c91a1f8572 Update solution settings. 2021-01-30 13:12:41 +01:00
Christian b103718bd9 Add Unit Tests. 2021-01-29 23:17:16 +01:00
Christian b88924bce5 Fixed wrong buffer size calculation. 2021-01-29 23:10:40 +01:00
Christian f3f1d81b52 Update docs. 2021-01-29 22:44:18 +01:00
Christian 031b60878d Merge pull request #1071 from muneebmajeed/master
Allow to respond with a reason code in PUBACK/PUBREC
2021-01-29 22:27:09 +01:00
HansM 1fbf20a569 Merge pull request #1076 from dagophil/bug/topic-filter-comparer-edge-case
The topic filter comparer now checks for e.g. "foo/" matching "foo/#".
2021-01-29 18:24:37 +01:00
Philip Schill b1bf7698aa The topic filter comparer now checks for e.g. "foo/" matching "foo/#". 2021-01-29 17:37:47 +01:00
Muneeb Majeed f8eaf02516 Allow to respond with a reason code in PUBACK/PUBREC 2021-01-26 17:18:52 -05:00
Christian df45540d6b Update docs. 2021-01-25 22:28:01 +01:00
Christian 2abcf6d8bc Update docs. 2021-01-25 22:27:45 +01:00
Christian c1001db450 Add packet inspector API. 2021-01-24 20:58:15 +01:00
SeppPenner 3d8d06f9b3 Hopefully completely reset the IManagedMqttClient file. 2021-01-15 20:43:57 +01:00
SeppPenner d804b805c1 Smaller changes on the comments. 2021-01-15 20:41:47 +01:00
SeppPenner bba04e2b5d Updated all code comments, should be ok now. 2021-01-15 20:34:33 +01:00
SeppPenner 5eab337e13 Removed not needed comments on client side (Server side is still missing). 2021-01-15 19:41:45 +01:00
Christian e1d39a3fef Fix build of MQTTnet Server. 2021-01-10 12:55:38 +01:00
Christian 3f5d627e9e Fix issues in QoS 2 handling. 2021-01-07 23:22:45 +01:00
SeppPenner 2047f44554 Added client documentation. 2021-01-03 21:46:53 +01:00
Christian 75845d6416 Fix cancellation issues when connecting. 2021-01-01 15:56:38 +01:00
Christian 74e952bb03 Merge remote-tracking branch 'origin/master' 2020-12-23 21:38:14 +01:00
Christian 3773048bc5 Update docs. 2020-12-23 21:38:08 +01:00
Christian f9aec0c067 Merge pull request #1048 from tobiasfrick/bugfix/cleandisconnect
Only set _cleanDisconnectInitiated if no disconnect is pending
2020-12-23 17:21:04 +01:00
Tobias Frick 91c8a15f65 Only set _cleanDisconnectInitiated if no disconnect is pending 2020-12-21 13:26:59 +01:00
Christian b95d6b08bc Update docs. 2020-12-19 13:45:13 +01:00
Christian a44ea74739 Merge pull request #1027 from StefanOssendorf/feature/Add-net5-tfm
.Net5.0 TFMs added
2020-12-19 13:02:11 +01:00
JanEggers 02e65bca67 fixed .net 5 build 2020-12-16 14:45:15 +01:00
Stefan Ossendorf 37b783908a .Net5.0 TFMs added
.Net5.0 TFMs added and related nuget packages updated.
2020-12-16 14:45:15 +01:00
Christian 72211ae85a Improve packet writer performance. 2020-12-14 21:17:22 +01:00
Christian 1f78a92c4d Remove timeout from send method. 2020-12-14 21:17:11 +01:00
Christian f92cfcd293 Add new multi threaded application message handler for server. 2020-12-13 11:36:31 +01:00
Christian 86256d1b10 Update README.md 2020-12-09 22:42:07 +01:00
Christian c3e65390ab Improve performance test. 2020-12-08 21:08:05 +01:00
Christian b0699003e2 Refactor code. 2020-12-08 21:07:58 +01:00
Christian 1214b15260 Update docs. 2020-11-29 11:43:29 +01:00
Christian 322067466b Fix wrong usage of client session items for undelivered messages. 2020-11-25 21:31:26 +01:00
Christian ef2ed1c897 Remove _WithoutAutoReconnect_ from managed client. 2020-11-25 21:18:36 +01:00
Christian 9fddfb3e77 Add UnitTests 2020-11-25 21:08:01 +01:00
Christian ec015e7620 Refactor naming for background tasks. 2020-11-25 21:07:50 +01:00
Christian 7738c5faa1 Refactor platform detection for unsupported features. 2020-11-21 15:43:32 +01:00
Christian 01bb09ff4b Remove obsolete files. 2020-11-17 22:23:08 +01:00
Christian e1494c1004 Update nuget packages. 2020-11-17 22:06:20 +01:00
Christian 1e2231c461 Improve Unit Tests. 2020-11-16 21:03:18 +01:00
Christian de0f5dc1f0 Update docs. 2020-11-16 20:57:31 +01:00
Christian a93f44217b Align different formats of log messages. 2020-11-16 20:47:44 +01:00
Christian f9c3014e52 Fix Unit Tests. 2020-11-15 14:34:59 +01:00
Christian 483c2d8b10 Fix Unit Tests. 2020-11-15 14:21:11 +01:00
Christian 02ab0e4903 Fix Unit Tests. 2020-11-15 14:17:18 +01:00
Christian 0bc25571e0 Fix Unit Tests 2020-11-15 12:46:10 +01:00
Christian Kratky 5b29b8cfd8 Fix Unit Tests. 2020-11-14 16:06:43 +01:00
Christian Kratky dd9dc054e8 Merge remote-tracking branch 'origin/master' 2020-11-14 15:31:06 +01:00
Christian Kratky b7c178b85c Fix Unit Tests. 2020-11-14 15:30:57 +01:00
Christian f8e29f8dc0 Update appveyor.yml 2020-11-14 15:24:49 +01:00
Christian 3d68951f68 Update MQTTnet.nuspec 2020-11-14 11:40:58 +01:00
Christian 7eb6d2d1e3 Merge pull request #1019 from chkr1011/feature/Single_Connection_Monitoring_Task
Feature/single connection monitoring task
2020-11-14 10:36:18 +01:00
Christian Kratky 0446635ed6 Add comments. 2020-11-14 10:35:08 +01:00
Christian Kratky 4514348821 Fix Unit Tests. 2020-11-13 23:24:45 +01:00
Christian Kratky 7b32abe625 Fix Unit Tests. 2020-11-13 23:11:01 +01:00
Christian Kratky 0febc12b3d Merge branch 'master' into feature/Single_Connection_Monitoring_Task 2020-11-13 20:36:51 +01:00
Christian Kratky d0a848c8b4 Improve performance. 2020-11-13 20:33:47 +01:00
Christian fe69f5a633 Merge pull request #1029 from mmuecke/patch-1
Added reference to Rx Wrapper for Managed Client
2020-11-12 11:26:22 +01:00
mmuecke ca95754fc3 Added reference to Rx Wrapper for Managed Client 2020-11-12 00:24:01 +00:00
Christian Kratky cc405c3e90 Update settings. 2020-11-11 21:12:37 +01:00
Christian Kratky 315206f05e Update settings. 2020-11-11 21:12:15 +01:00
Christian Kratky f252374437 Extend support for topic alias. 2020-11-11 21:12:04 +01:00
Christian Kratky 38d3531db6 Refactor code. 2020-11-11 21:11:49 +01:00
Christian Kratky da5a172504 Refactor code. 2020-11-09 21:30:36 +01:00
Christian Kratky f894622884 Merge branch 'master' into feature/Single_Connection_Monitoring_Task 2020-11-09 21:29:38 +01:00
Christian 88b0af9b21 Merge pull request #1023 from StefanOssendorf/fix/compile-error
Fix: Compile error, cancellation token removed since the method does not support it
2020-11-09 21:11:48 +01:00
Christian Kratky e1bf9de347 Add support for topic alias. 2020-11-09 21:10:05 +01:00
Christian Kratky 6c15693456 Add support for TopicAlias in application message builder. 2020-11-09 17:22:32 +01:00
Stefan Ossendorf 4608930a37 Cancellation token removed since the method does not support it
Cancellation token removed because the SendAsync() method does not support it.
2020-11-08 14:31:01 +01:00
Christian Kratky eaf1195f95 Cleanup Unit Tests. 2020-11-06 21:00:25 +01:00
Christian Kratky a35dee9352 Update docs. 2020-11-06 20:03:33 +01:00
Christian 6cef746ed9 Merge pull request #1021 from nayato/max-qos
Add support for Maximum QoS property on CONNACK
2020-11-06 20:00:09 +01:00
Max Gortman 344cf674e6 PR fixes 2020-11-05 16:55:50 -08:00
Christian Kratky 26af205ab0 Update docs. 2020-11-04 21:29:26 +01:00
Christian Kratky 06c9d27c87 Fixed bug in PubRel packet generation (MQTTv5 only). 2020-11-04 21:28:40 +01:00
Max Gortman 14224e4180 Add support for Maximum QoS property on CONNACK 2020-11-03 12:19:38 -08:00
Christian Kratky b954c9512e Fix swagger configuration. 2020-11-02 22:03:47 +01:00
Christian Kratky fb2be79673 Remove not used timeout for receiving. 2020-11-02 21:45:23 +01:00
Christian Kratky 44b7133a1c Improve keep alive tests. 2020-11-02 21:17:31 +01:00
Christian Kratky 2f0513212e Merge branch 'master' into feature/Single_Connection_Monitoring_Task 2020-11-01 12:35:59 +01:00
Christian Kratky 1a9460776b Update docs. 2020-11-01 12:03:19 +01:00
Christian Kratky 2716db04e3 Move the RPC topic validation to the topic generation strategy. 2020-11-01 12:00:12 +01:00
Christian Kratky 3e4f4a59a8 Improve Unit Tests. 2020-11-01 11:58:43 +01:00
Christian Kratky 24f0ea01f5 Improve session takeover and keep alive handling. 2020-10-31 20:59:41 +01:00
Christian Kratky 1625aadae9 Improve disconnect reason transfer to client (especially for session take over). 2020-10-31 11:43:14 +01:00
Christian Kratky e800901b4c Merge branch 'master' into feature/Single_Connection_Monitoring_Task 2020-10-31 11:33:54 +01:00
Christian Kratky 34ae54d542 Improve Unit Tests. 2020-10-31 11:33:44 +01:00
Christian Kratky 838d50c1d3 Remove file which is not part of the solution. 2020-10-31 11:27:46 +01:00
Christian Kratky 558d281a33 Refactor TLS 1.3 support. 2020-10-31 11:23:14 +01:00
Christian Kratky f8c956a9e2 Merge remote-tracking branch 'origin/master' 2020-10-31 11:14:07 +01:00
Christian 9a6823597e Merge pull request #1020 from Dvergatal/master
Added support for Tls1.3
2020-10-31 11:13:50 +01:00
Christian Kratky 42c1155bee Fix nullref exception in connection handler. 2020-10-30 23:51:11 +01:00
Christian Kratky f773257c86 Improve exception handling in AsyncQueue. 2020-10-30 20:43:39 +01:00
Christian Kratky 4e9df98817 Improve test environment. 2020-10-30 20:43:02 +01:00
Christian Kratky 3208045baa Extend application message builder. 2020-10-30 20:42:52 +01:00
Piotr Łobacz 7274f4390c added support for Tls1.3 protocol 2020-10-30 12:45:57 +01:00
Dvergatal 11c8abfd54 Merge pull request #1 from chkr1011/master
Merging with mainline.
2020-10-30 11:16:09 +01:00
Christian Kratky f606a5996b Merge remote-tracking branch 'origin/master' 2020-10-30 00:26:40 +01:00
Christian Kratky 58ad599783 Improve disconnect code of client. 2020-10-30 00:23:45 +01:00
Christian Kratky ccd102a59f Refactor code. 2020-10-30 00:21:08 +01:00
Christian Kratky 05101f0e0e Improve exception handling of AsyncLock. 2020-10-30 00:19:57 +01:00
Christian Kratky f9630ef347 Improve internal session/connection management and connection takeover. 2020-10-30 00:19:30 +01:00
Christian Kratky c5b7735a2c Improve cancellation token handling for client connections (Server). 2020-10-29 20:02:03 +01:00
HansM 12fca2f05a Merge pull request #1015 from Jay424/patch-1
Fixed evaluation of IgnoreCertificateRevocationErrors to correctly se…
2020-10-29 11:34:23 +01:00
Christian Kratky b7fb0c1002 Refactor package object model. 2020-10-28 23:11:12 +01:00
Jay424 3ea281ff84 Updated release notes 2020-10-28 17:58:48 +01:00
Jay424 c778cd7a0f Fixed evaluation of IgnoreCertificateRevocationErrors to correctly set SslClientAuthenticationOptions.CertificateRevocationCheckMode
The if was just flipped.
2020-10-28 17:14:09 +01:00
Christian Kratky adce5662eb Prepare docs for next release. 2020-10-24 12:22:03 +02:00
Christian Kratky a26b641284 Fix broken connection takeover. 2020-10-15 01:01:35 +02:00
Christian Kratky 7be3685850 Merge remote-tracking branch 'origin/master' 2020-10-14 23:10:40 +02:00
Christian Kratky 1a6be47144 Update docs. 2020-10-14 23:10:13 +02:00
Christian 5d73de2a86 Merge pull request #1004 from JanEggers/fix#990
improved behavior when multiple connections fight over a session
2020-10-14 21:18:36 +02:00
JanEggers 2eaebe0e6a removed todo 2020-10-13 15:59:39 +02:00
JanEggers d7b552fce2 renaming and added cast 2020-10-13 15:57:21 +02:00
Christian bea6d75251 Merge pull request #993 from chkr1011/client-disconnected-handler
Tried to add a MqttServerOptions fluent method for the client disconn…
2020-10-12 20:38:15 +02:00
JanEggers f7db00c08e improved behavior when multiple connections fight over a session 2020-10-11 16:41:13 +02:00
Christian Kratky f259bbcc9b ManagedClient: Expose internal client. 2020-10-07 19:38:42 +02:00
SeppPenner 252772007b Changed MqttServerClientDisconnectedHandlerDelegate back again. 2020-09-27 17:43:06 +02:00
SeppPenner 21a9974e5d Tried to add a MqttServerOptions fluent method for the client disconnected handler. 2020-09-21 18:23:11 +02:00
Christian Kratky d63143bdc9 ManagedClient: Add new option for disabling auto reconnect. 2020-09-14 23:49:27 +02:00
Christian Kratky aad9eac361 Refactor AWS integration. 2020-09-14 22:01:07 +02:00
Christian Kratky e8a683b936 Merge remote-tracking branch 'origin/master' 2020-09-14 21:47:38 +02:00
Christian Kratky e475e36ce7 Update docs. 2020-09-14 21:47:36 +02:00
Christian 611c230da2 Merge pull request #981 from henning-krause/master
Added ApplicationProtocols support to the TLS options to support AWS MQTT on port 443
2020-09-14 21:46:18 +02:00
Krause, Henning 18839de083 Removed -prelease switch from build 2020-09-14 19:14:39 +02:00
Krause, Henning 194664bc9c Reverted bogus change 2020-09-14 19:13:49 +02:00
Krause, Henning 10a33fd153 Removed -prelease switch from build 2020-09-14 19:12:30 +02:00
Christian Kratky 868aa7d89a Merge remote-tracking branch 'origin/master' 2020-09-14 10:02:40 +02:00
Christian Kratky 1f8792cf87 Update docs. 2020-09-14 09:59:19 +02:00
Christian 7bb99c8b37 Merge pull request #979 from msallin/bugfix/978/ClientMessageQueueInterceptor
Bugfix/978/client message queue interceptor
2020-09-14 09:48:13 +02:00
Marc Sallin 7f5504b01b Fix wrong QoS when using MqttClientMessageQueueInterceptor #978 2020-09-04 09:53:34 +02:00
Marc Sallin fd789417e7 Extend MqttClientMessageQueueInterceptorContext to expose SubscriptionQualityOfServiceLevel 2020-09-04 09:53:34 +02:00
Marc Sallin f1f9924bce Expose WithClientMessageQueueInterceptor 2020-09-04 09:53:34 +02:00
Marc Sallin b0c603082b Rename "QualityOfServiceLevel" to SubscriptionQualityOfServiceLevel where appropriated to make the difference between the two different QoS fields more clear 2020-09-04 09:53:34 +02:00
Krause, Henning f3f10e9f65 Added missing nuget instructions for the build 2020-09-01 19:44:52 +02:00
Krause, Henning beb228cc05 Fixed issues from code review 2020-08-29 13:51:16 +02:00
Krause, Henning 7a7f0640ba Reverted solution file 2020-08-28 19:12:30 +02:00
Krause, Henning 55d009ce6b Reverted solution file 2020-08-28 19:11:37 +02:00
Krause, Henning e23ac4f703 Removed test program 2020-08-28 19:08:45 +02:00
Krause, Henning fc367ddb64 Added support to set TLS Application Protocols 2020-08-28 19:07:31 +02:00
Christian Kratky cf217034fa Fix broken ClientWasConnected detection. 2020-08-22 20:44:24 +02:00
237 changed files with 4339 additions and 3676 deletions
+81 -2
View File
@@ -1,5 +1,84 @@
[*.cs]
[*.cs]
# CA1031: Do not catch general exception types
dotnet_diagnostic.CA1031.severity = none
# Microsoft .NET properties
csharp_new_line_before_members_in_object_initializers = false
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_naming_rule.constants_rule.severity = warning
dotnet_naming_rule.constants_rule.style = upper_camel_case_style
dotnet_naming_rule.constants_rule.symbols = constants_symbols
dotnet_naming_rule.public_fields_rule.severity = warning
dotnet_naming_rule.public_fields_rule.style = lower_camel_case_style
dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols
dotnet_naming_rule.static_readonly_rule.severity = warning
dotnet_naming_rule.static_readonly_rule.style = upper_camel_case_style
dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
dotnet_naming_style.lower_camel_case_style.required_prefix = _
dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
dotnet_naming_symbols.constants_symbols.applicable_kinds = field
dotnet_naming_symbols.constants_symbols.required_modifiers = const
dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field
dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field
dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
# ReSharper properties
resharper_autodetect_indent_settings = true
resharper_default_private_modifier = implicit
resharper_place_accessorholder_attribute_on_same_line = false
resharper_use_indent_from_vs = false
# ReSharper inspection severities
resharper_arrange_redundant_parentheses_highlighting = hint
resharper_arrange_this_qualifier_highlighting = hint
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
resharper_built_in_type_reference_style_highlighting = hint
resharper_redundant_base_qualifier_highlighting = warning
resharper_suggest_var_or_type_built_in_types_highlighting = hint
resharper_suggest_var_or_type_elsewhere_highlighting = hint
resharper_suggest_var_or_type_simple_types_highlighting = hint
resharper_web_config_module_not_resolved_highlighting = warning
resharper_web_config_type_not_resolved_highlighting = warning
resharper_web_config_wrong_module_highlighting = warning
[*]
charset = utf-8
end_of_line = crlf
trim_trailing_whitespace = false
insert_final_newline = false
indent_style = space
indent_size = 4
[{*.har,*.inputactions,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}]
indent_style = space
indent_size = 2
[{*.yaml,*.yml}]
indent_style = space
indent_size = 2
[{tsconfig.app.json,tsconfig.e2e.json,tsconfig.json,tsconfig.lib.json,tsconfig.spec.json}]
indent_style = space
indent_size = 2
[*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,hlsl,hlsli,hlslinc,master,nuspec,paml,razor,resw,resx,shader,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}]
indent_style = space
indent_size = 4
tab_width = 4
+11 -4
View File
@@ -19,10 +19,14 @@
<dependency id="MQTTnet" version="$nugetVersion" />
<dependency id="Microsoft.AspNetCore.Http.Connections" version="1.0.0" />
</group>
<group targetFramework="netcoreapp3.1">
<dependency id="MQTTnet" version="$nugetVersion" />
</group>
<group targetFramework="net5.0">
<dependency id="MQTTnet" version="$nugetVersion" />
</group>
</dependencies>
</metadata>
@@ -33,10 +37,13 @@
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\" />
<!-- .NET Core App 3.1 -->
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.*" target="lib\netcoreapp3.1\"/>
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.*" target="lib\netcoreapp3.1\" />
</files>
</package>
@@ -13,7 +13,7 @@
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<dependencies>
<dependency id="MQTTnet" version="$nugetVersion" />
</dependencies>
@@ -26,20 +26,23 @@
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\" />
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.0\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.0\" />
<!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\uap10.0\MQTTnet.Extensions.ManagedClient.*" target="lib\uap10.0\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\uap10.0\MQTTnet.Extensions.ManagedClient.*" target="lib\uap10.0\" />
<!-- .NET Framework -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\" />
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\" />
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files>
+9 -6
View File
@@ -13,7 +13,7 @@
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<dependencies>
<dependency id="MQTTnet" version="$nugetVersion" />
</dependencies>
@@ -26,20 +26,23 @@
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\" />
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\" />
<!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\" />
<!-- .NET Framework -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\" />
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\" />
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files>
@@ -13,7 +13,7 @@
<description>This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.</description>
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<dependencies>
<group targetFramework="netstandard1.3">
<dependency id="MQTTnet" version="$nugetVersion" />
@@ -41,20 +41,23 @@
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard1.3\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard1.3\"/>
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard1.3\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard1.3\" />
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.0\"/>
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.0\" />
<!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\uap10.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\uap10.0\"/>
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\uap10.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\uap10.0\" />
<!-- .NET Framework -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net452\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net452\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net452\" />
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net461\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net461\"/>
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net461\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net461\" />
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files>
+41 -19
View File
@@ -12,25 +12,35 @@
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</description>
<releaseNotes>
* [LowLevelClient] Fixed a null reference exception when connecting to a not existing server (thanks to @SGStino).
* [RcpClient] Adjusted some namespaces (BREAKING CHANGE!).
* [AspNetCore] Adjusted some namespaces (BREAKING CHANGE!).
* [Server] Adjusted some namespaces (BREAKING CHANGE!).
* [Server] Added state checks (throw if not started etc.) for most server APIs.
* [Server] Exposed real X509Certificate2 (instead byte array) to TLS options (thanks to @borigas).
* [Server] Fixed memory leak with TCP sockets (MqttServer is now Disposable!) (BREAKING CHANGE!).
* [Core] Fixed a null reference exception in the MqttTcpChannel with WriteAsync and ReadAsync.
* [Core] Added server interceptor for undelivered messages (thanks to @cshark-inator).
* [nuget] Added support for SourceLink (thanks to @JTOne123).
* [Core] Added .NET 5.0 TFMs (thanks to @StefanOssendorf, @JanEggers).
* [Core] Added support for TLS 1.3 (requires .NET Core 3.1+) (thanks to @Dvergatal).
* [Core] Added support for _MaximumQoS_ transmit when using MQTTv5 (thanks to @nayato).
* [Core] Aligned the format of some log messages.
* [Client] Fixed an issue with connection state detection (thanks to @tobiasfrick).
* [Client] Fixed an issue when receiving partial QoS 2 packets after a reconnect.
* [Client] Added new API for raw packet inspection.
* [ManagedClient] Removed _AutoReconnect_ since this does no longer work (BREAKING CHANGE!).
* [RpcClient] Moved the RPC topic validation to the topic generation strategy.
* [RcpClient] Added interface for _MqttRpcClient_.
* [Server] Reduced async tasks count by moving dedicated keep alive tasks per connection to shared one.
* [Server] Session takeover and keep alive timeout are now properly set in DISCONNECT packet.
* [Server] Fixed bug in PubRel packet generation (MQTTv5 only).
* [Server] Improved message processing performance (+ ~5%).
* [Server] Fix wrong usage of client session items for undelivered messages.
* [Server] Allow to respond with a reason code in PUBACK/PUBREC (thanks to @muneebmajeed).
* [Server] Fixed topic filter comparer on edge cases, e.g. "foo/" matching "foo/#" (thanks to @dagophil).
* [Core] Added code documentation.
Git commit: $gitCommit
</releaseNotes>
<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 Blazor</tags>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin Blazor</tags>
<dependencies>
<group targetFramework="netstandard1.3">
<dependency id="NETStandard.Library" version="1.3.0" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="NETStandard.Library" version="2.0.0" />
@@ -44,6 +54,12 @@
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="netcoreapp3.1">
<dependency id="NETStandard.Library" version="2.0.0" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" />
</group>
@@ -57,23 +73,29 @@
<!-- Images -->
<file src="..\Images\Logo_128x128.png" target="images\" />
<!-- .NET 5.0 -->
<file src="..\Source\MQTTnet\bin\Release\net5.0\MQTTnet.*" target="lib\net5.0\" />
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\" />
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\" />
<!-- .NET Standard 2.1 -->
<file src="..\Source\MQTTnet\bin\Release\netstandard2.1\MQTTnet.*" target="lib\netstandard2.1\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.1\MQTTnet.*" target="lib\netstandard2.1\" />
<!-- .NET Standard 3.1 -->
<file src="..\Source\MQTTnet\bin\Release\netcoreapp3.1\MQTTnet.*" target="lib\netcoreapp3.1\" />
<!-- Universal Windows -->
<file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/>
<file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\" />
<!-- .NET Framework -->
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\" />
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
<file src="..\Source\MQTTnet\bin\Release\net461\MQTTnet.*" target="lib\net461\"/>
<file src="..\Source\MQTTnet\bin\Release\net461\MQTTnet.*" target="lib\net461\" />
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files>
+10 -4
View File
@@ -29,6 +29,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the ASP.NET Core 2.0 extension
@@ -63,6 +64,10 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
$gitCommit = git log -1 --format=%h
Copy-Item MQTTnet.nuspec -Destination MQTTnet.nuspec.old -Force
(Get-Content MQTTnet.nuspec) -replace '\$gitCommit', $gitCommit | Set-Content MQTTnet.nuspec
Copy-Item MQTTnet.AspNetCore.nuspec -Destination MQTTnet.AspNetCore.nuspec.old -Force
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
Copy-Item MQTTnet.Extensions.Rpc.nuspec -Destination MQTTnet.Extensions.Rpc.nuspec.old -Force
@@ -79,6 +84,7 @@ New-Item -ItemType Directory -Force -Path .\NuGet
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.Extensions.WebSocket4Net.nuspec -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -OutputDir "NuGet" -Version $nugetVersion
Move-Item MQTTnet.nuspec.old -Destination MQTTnet.nuspec -Force
Move-Item MQTTnet.AspNetCore.nuspec.old -Destination MQTTnet.AspNetCore.nuspec -Force
Move-Item MQTTnet.Extensions.Rpc.nuspec.old -Destination MQTTnet.Extensions.Rpc.nuspec -Force
Move-Item MQTTnet.Extensions.ManagedClient.nuspec.old -Destination MQTTnet.Extensions.ManagedClient.nuspec -Force
@@ -89,7 +95,7 @@ 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
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Portable-v$nugetVersion.zip"
@@ -100,7 +106,7 @@ 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
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-x64 --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-x64\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-x64-v$nugetVersion.zip"
@@ -111,7 +117,7 @@ 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
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-arm --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\linux-ARM\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-ARM-v$nugetVersion.zip"
@@ -122,7 +128,7 @@ 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
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime win-x64 --framework net5.0
$source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\win-x64\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Windows-x64-v$nugetVersion.zip"
+3 -29
View File
@@ -9,8 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.UniversalWindows", "Tests\MQTTnet.TestApp.UniversalWindows\MQTTnet.TestApp.UniversalWindows.csproj", "{FF1F72D6-9524-4422-9497-3CC0002216ED}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
@@ -57,11 +55,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Apps", "Apps", "{A56E3128-1
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Test.BlazorApp.Server", "Tests\MQTTnet.Test.BlazorApp\Server\MQTTnet.Test.BlazorApp.Server.csproj", "{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Test.BlazorApp.Server", "Tests\MQTTnet.Test.BlazorApp\Server\MQTTnet.Test.BlazorApp.Server.csproj", "{A9662AF3-3520-4BF8-9DFF-C55C0C33D08F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Test.BlazorApp.Client", "Tests\MQTTnet.Test.BlazorApp\Client\MQTTnet.Test.BlazorApp.Client.csproj", "{D260D63D-7902-4C55-9665-84C5CACBBB24}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Test.BlazorApp.Client", "Tests\MQTTnet.Test.BlazorApp\Client\MQTTnet.Test.BlazorApp.Client.csproj", "{D260D63D-7902-4C55-9665-84C5CACBBB24}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Test.BlazorApp.Shared", "Tests\MQTTnet.Test.BlazorApp\Shared\MQTTnet.Test.BlazorApp.Shared.csproj", "{DDB069BA-6E1A-48C7-B374-5D903B553CAD}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Test.BlazorApp.Shared", "Tests\MQTTnet.Test.BlazorApp\Shared\MQTTnet.Test.BlazorApp.Shared.csproj", "{DDB069BA-6E1A-48C7-B374-5D903B553CAD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -91,29 +89,6 @@ Global
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.ActiveCfg = Debug|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Build.0 = Debug|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Deploy.0 = Debug|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.ActiveCfg = Debug|x64
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Build.0 = Debug|x64
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Deploy.0 = Debug|x64
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.ActiveCfg = Debug|x86
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.Build.0 = Debug|x86
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.Deploy.0 = Debug|x86
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|Any CPU.Build.0 = Release|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.ActiveCfg = Release|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.Build.0 = Release|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.Deploy.0 = Release|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.ActiveCfg = Release|x64
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.Build.0 = Release|x64
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.Deploy.0 = Release|x64
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.ActiveCfg = Release|x86
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.Build.0 = Release|x86
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.Deploy.0 = Release|x86
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
@@ -328,7 +303,6 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {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}
+2 -1
View File
@@ -11,4 +11,5 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+26 -6
View File
@@ -19,7 +19,7 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
### General
* Async support
* TLS 1.2 support for client and server (but not UWP servers)
* TLS support for client and server (but not UWP servers)
* Extensible communication channels (e.g. In-Memory, TCP, TCP+TLS, WS)
* Lightweight (only the low level implementation of MQTT, no overhead)
* Performance optimized (processing ~70.000 messages / second)*
@@ -64,15 +64,32 @@ _MQTTnet Server_ is a standalone cross platform MQTT server (like mosquitto) bas
## Supported frameworks
* .NET 5.0+
* .NET Standard 1.3+
* .NET Core 1.1+
* .NET Core App 1.1+
* .NET Framework 4.5.2+ (x86, x64, AnyCPU)
* .NET Framework 4.5.2+
* Mono 5.2+
* Universal Windows Platform (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU, Windows 10 IoT Core)
* Universal Windows Platform (UWP) 10.0.10240+ (Windows 10 IoT Core)
* Xamarin.Android 7.5+
* Xamarin.iOS 10.14+
* Blazor WebAssembly 3.2.0
* Blazor WebAssembly 3.2.0+
## Supported platforms
* x86
* x64
* AnyCPU
* ARM
## Supported OS
* Windows
* Windows 10 IoT Core
* Linux (Ubuntu, Raspbian etc.)
* macOS
* Android
* iOS
## Supported MQTT versions
@@ -97,11 +114,14 @@ Also additions and updates in the Wiki are welcome.
This library is used in the following projects:
* Azure Functions MQTT Bindings, [CaseOnline.Azure.WebJobs.Extensions.Mqtt](https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt/)
* Azure Functions MQTT Bindings, <https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt>)
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
* MQTT Client Rx (Managed Client Wrapper for Reactive Extensions, <https://github.com/mmuecke/RxMQTTnet>)
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
* MQTTnet App (Cross platform client application for MQTT debugging, inspection etc., <https://github.com/chkr1011/MQTTnet.App>)
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
* SparkplugNet (Sparkplug library for .Net, <https://github.com/SeppPenner/SparkplugNet>)
Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents.
@@ -111,7 +131,7 @@ If you use this library and want to see your project here please create a pull r
MIT License
MQTTnet Copyright (c) 2016-2020 Christian Kratky
MQTTnet Copyright (c) 2016-2021 Christian Kratky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -40,7 +40,7 @@ namespace MQTTnet.AspNetCore.Client.Tcp
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
}
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
public override ValueTask DisposeAsync()
#else
public Task DisposeAsync()
@@ -53,7 +53,7 @@ namespace MQTTnet.AspNetCore.Client.Tcp
_socket?.Dispose();
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
return base.DisposeAsync();
}
@@ -12,6 +12,7 @@ namespace MQTTnet.AspNetCore.Extensions
#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
#if NETCOREAPP3_1 || NETCOREAPP2_1 || NETSTANDARD
public static void MapMqtt(this ConnectionsRouteBuilder connection, PathString path)
{
connection.MapConnectionHandler<MqttConnectionHandler>(path, options =>
@@ -19,5 +20,6 @@ namespace MQTTnet.AspNetCore.Extensions
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
});
}
#endif
}
}
@@ -1,5 +1,5 @@
#if NETCOREAPP3_1
#if NETCOREAPP3_1 || NET5_0
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks>
<Product />
<Company />
<Authors />
@@ -21,11 +21,11 @@
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' ">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1'">
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.1' and '$(TargetFramework)' != 'net5.0' ">
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
</ItemGroup>
@@ -66,10 +66,10 @@ namespace MQTTnet.AspNetCore
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public long BytesSent { get; set; }
public long BytesReceived { get; set; }
public Action ReadingPacketStartedCallback { get; set; }
public Action ReadingPacketCompletedCallback { get; set; }
public bool IsReadingPacket { get; private set; }
IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
@@ -92,7 +92,7 @@ namespace MQTTnet.AspNetCore
return Task.CompletedTask;
}
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
public async Task<MqttBasePacket> ReceivePacketAsync(CancellationToken cancellationToken)
{
var input = Connection.Transport.Input;
@@ -128,7 +128,7 @@ namespace MQTTnet.AspNetCore
else
{
// we did receive something but the message is not yet complete
ReadingPacketStartedCallback?.Invoke();
IsReadingPacket = true;
}
}
else if (readResult.IsCompleted)
@@ -147,14 +147,14 @@ namespace MQTTnet.AspNetCore
}
catch (Exception e)
{
// completing the cannels makes sure that there is no more data read after a protocol error
// completing the channel makes sure that there is no more data read after a protocol error
_input?.Complete(e);
_output?.Complete(e);
throw;
}
finally
{
ReadingPacketCompletedCallback?.Invoke();
IsReadingPacket = false;
}
cancellationToken.ThrowIfCancellationRequested();
@@ -167,7 +167,7 @@ namespace MQTTnet.AspNetCore
BytesSent = 0;
}
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
var formatter = PacketFormatterAdapter;
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
@@ -180,6 +180,7 @@ namespace MQTTnet.AspNetCore
{
BytesSent += msg.Length;
}
PacketFormatterAdapter.FreeBuffer();
}
}
@@ -49,7 +49,7 @@ namespace MQTTnet.AspNetCore
var formatter = new MqttPacketFormatterAdapter(writer);
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _rootLogger))
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _rootLogger))
{
await clientHandler(channelAdapter).ConfigureAwait(false);
}
@@ -4,30 +4,46 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Client;
namespace MQTTnet.Extensions.ManagedClient
{
public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
{
bool IsStarted { get; }
bool IsConnected { get; }
int PendingApplicationMessagesCount { get; }
IManagedMqttClientOptions Options { get; }
/// <summary>
/// Gets the internally used MQTT client.
/// This property should be used with caution because manipulating the internal client might break the managed client.
/// </summary>
IMqttClient InternalClient { get; }
bool IsStarted { get; }
bool IsConnected { get; }
int PendingApplicationMessagesCount { get; }
IManagedMqttClientOptions Options { get; }
IMqttClientConnectedHandler ConnectedHandler { get; set; }
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
IConnectingFailedHandler ConnectingFailedHandler { get; set; }
ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
Task StartAsync(IManagedMqttClientOptions options);
Task StopAsync();
Task PingAsync(CancellationToken cancellationToken);
Task SubscribeAsync(IEnumerable<MqttTopicFilter> topicFilters);
Task UnsubscribeAsync(IEnumerable<string> topics);
Task PublishAsync(ManagedMqttApplicationMessage applicationMessages);
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
<Product />
@@ -18,7 +18,7 @@ namespace MQTTnet.Extensions.ManagedClient
{
public class ManagedMqttClient : Disposable, IManagedMqttClient
{
private readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>();
readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>();
/// <summary>
/// The subscriptions are managed in 2 separate buckets:
@@ -27,51 +27,55 @@ namespace MQTTnet.Extensions.ManagedClient
/// 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);
readonly Dictionary<string, MqttQualityOfServiceLevel> _reconnectSubscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
readonly HashSet<string> _unsubscriptions = new HashSet<string>();
readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
private readonly IMqttClient _mqttClient;
private readonly IMqttNetScopedLogger _logger;
readonly IMqttNetScopedLogger _logger;
private readonly AsyncLock _messageQueueLock = new AsyncLock();
readonly AsyncLock _messageQueueLock = new AsyncLock();
private CancellationTokenSource _connectionCancellationToken;
private CancellationTokenSource _publishingCancellationToken;
private Task _maintainConnectionTask;
CancellationTokenSource _connectionCancellationToken;
CancellationTokenSource _publishingCancellationToken;
Task _maintainConnectionTask;
private ManagedMqttClientStorageManager _storageManager;
ManagedMqttClientStorageManager _storageManager;
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
{
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
InternalClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateScopedLogger(nameof(ManagedMqttClient));
}
public bool IsConnected => _mqttClient.IsConnected;
public bool IsConnected => InternalClient.IsConnected;
public bool IsStarted => _connectionCancellationToken != null;
public IMqttClient InternalClient { get; }
public int PendingApplicationMessagesCount => _messageQueue.Count;
public IManagedMqttClientOptions Options { get; private set; }
public IMqttClientConnectedHandler ConnectedHandler
{
get => _mqttClient.ConnectedHandler;
set => _mqttClient.ConnectedHandler = value;
get => InternalClient.ConnectedHandler;
set => InternalClient.ConnectedHandler = value;
}
public IMqttClientDisconnectedHandler DisconnectedHandler
{
get => _mqttClient.DisconnectedHandler;
set => _mqttClient.DisconnectedHandler = value;
get => InternalClient.DisconnectedHandler;
set => InternalClient.DisconnectedHandler = value;
}
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler
{
get => _mqttClient.ApplicationMessageReceivedHandler;
set => _mqttClient.ApplicationMessageReceivedHandler = value;
get => InternalClient.ApplicationMessageReceivedHandler;
set => InternalClient.ApplicationMessageReceivedHandler = value;
}
public IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
@@ -89,14 +93,13 @@ 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 (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
Options = options;
if (Options.Storage != null)
if (options.Storage != null)
{
_storageManager = new ManagedMqttClientStorageManager(Options.Storage);
_storageManager = new ManagedMqttClientStorageManager(options.Storage);
var messages = await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
foreach (var message in messages)
@@ -105,10 +108,12 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
_connectionCancellationToken = new CancellationTokenSource();
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
_connectionCancellationToken = cancellationTokenSource;
_maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.Token);
_maintainConnectionTask.Forget(_logger);
_maintainConnectionTask = Task.Run(() => MaintainConnectionAsync(cancellationToken), cancellationToken);
_maintainConnectionTask.RunInBackground(_logger);
_logger.Info("Started");
}
@@ -131,7 +136,7 @@ namespace MQTTnet.Extensions.ManagedClient
public Task PingAsync(CancellationToken cancellationToken)
{
return _mqttClient.PingAsync(cancellationToken);
return InternalClient.PingAsync(cancellationToken);
}
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
@@ -218,6 +223,7 @@ namespace MQTTnet.Extensions.ManagedClient
_unsubscriptions.Remove(topicFilter.Topic);
}
}
_subscriptionsQueuedSignal.Release();
return Task.FromResult(0);
@@ -257,13 +263,14 @@ namespace MQTTnet.Extensions.ManagedClient
_messageQueue.Dispose();
_messageQueueLock.Dispose();
_mqttClient.Dispose();
InternalClient.Dispose();
_subscriptionsQueuedSignal.Dispose();
}
base.Dispose(disposing);
}
private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
async Task MaintainConnectionAsync(CancellationToken cancellationToken)
{
try
{
@@ -285,7 +292,14 @@ namespace MQTTnet.Extensions.ManagedClient
{
try
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.CommunicationTimeout))
{
await InternalClient.DisconnectAsync(disconnectTimeout.Token).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
_logger.Warning("Timeout while sending DISCONNECT packet.");
}
catch (Exception exception)
{
@@ -294,7 +308,9 @@ namespace MQTTnet.Extensions.ManagedClient
_logger.Info("Stopped");
}
_reconnectSubscriptions.Clear();
lock (_subscriptions)
{
_subscriptions.Clear();
@@ -303,11 +319,11 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
private async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
{
try
{
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false);
var connectionState = await ReconnectIfRequiredAsync(cancellationToken).ConfigureAwait(false);
if (connectionState == ReconnectionResult.NotConnected)
{
StopPublishing();
@@ -346,11 +362,11 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested && _mqttClient.IsConnected)
while (!cancellationToken.IsCancellationRequested && InternalClient.IsConnected)
{
// Peek at the message without dequeueing in order to prevent the
// possibility of the queue growing beyond the configured cap.
@@ -384,12 +400,12 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
private async Task TryPublishQueuedMessageAsync(ManagedMqttApplicationMessage message)
async Task TryPublishQueuedMessageAsync(ManagedMqttApplicationMessage message)
{
Exception transmitException = null;
try
{
await _mqttClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
await InternalClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
{
@@ -411,7 +427,7 @@ namespace MQTTnet.Extensions.ManagedClient
{
transmitException = exception;
_logger.Warning(exception, $"Publishing application ({message.Id}) message failed.");
_logger.Warning(exception, "Publishing application message ({0}) failed.", message.Id);
if (message.ApplicationMessage.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{
@@ -436,7 +452,7 @@ namespace MQTTnet.Extensions.ManagedClient
catch (Exception exception)
{
transmitException = exception;
_logger.Error(exception, $"Error while publishing application message ({message.Id}).");
_logger.Error(exception, "Error while publishing application message ({0}).", message.Id);
}
finally
{
@@ -449,9 +465,10 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
private async Task PublishSubscriptionsAsync(TimeSpan timeout, CancellationToken cancellationToken)
async Task PublishSubscriptionsAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
var endTime = DateTime.UtcNow + timeout;
while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false))
{
List<MqttTopicFilter> subscriptions;
@@ -470,7 +487,7 @@ namespace MQTTnet.Extensions.ManagedClient
continue;
}
_logger.Verbose($"Publishing subscriptions ({subscriptions.Count} subscriptions and {unsubscriptions.Count} unsubscriptions)");
_logger.Verbose("Publishing {0} subscriptions and {1} unsubscriptions)", subscriptions.Count, unsubscriptions.Count);
foreach (var unsubscription in unsubscriptions)
{
@@ -486,12 +503,12 @@ namespace MQTTnet.Extensions.ManagedClient
{
if (unsubscriptions.Any())
{
await _mqttClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false);
await InternalClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false);
}
if (subscriptions.Any())
{
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
await InternalClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
}
}
catch (Exception exception)
@@ -501,7 +518,7 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
private async Task PublishReconnectSubscriptionsAsync()
async Task PublishReconnectSubscriptionsAsync()
{
_logger.Info("Publishing subscriptions at reconnect");
@@ -510,7 +527,7 @@ namespace MQTTnet.Extensions.ManagedClient
if (_reconnectSubscriptions.Any())
{
var subscriptions = _reconnectSubscriptions.Select(i => new MqttTopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value });
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
await InternalClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
}
}
catch (Exception exception)
@@ -519,7 +536,7 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
private async Task HandleSubscriptionExceptionAsync(Exception exception)
async Task HandleSubscriptionExceptionAsync(Exception exception)
{
_logger.Warning(exception, "Synchronizing subscriptions failed.");
@@ -530,16 +547,16 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
private async Task<ReconnectionResult> ReconnectIfRequiredAsync()
async Task<ReconnectionResult> ReconnectIfRequiredAsync(CancellationToken cancellationToken)
{
if (_mqttClient.IsConnected)
if (InternalClient.IsConnected)
{
return ReconnectionResult.StillConnected;
}
try
{
var result = await _mqttClient.ConnectAsync(Options.ClientOptions).ConfigureAwait(false);
var result = await InternalClient.ConnectAsync(Options.ClientOptions, cancellationToken).ConfigureAwait(false);
return result.IsSessionPresent ? ReconnectionResult.Recovered : ReconnectionResult.Reconnected;
}
catch (Exception exception)
@@ -554,34 +571,47 @@ namespace MQTTnet.Extensions.ManagedClient
}
}
private void StartPublishing()
void StartPublishing()
{
if (_publishingCancellationToken != null)
{
StopPublishing();
}
var cts = new CancellationTokenSource();
_publishingCancellationToken = cts;
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
_publishingCancellationToken = cancellationTokenSource;
Task.Run(() => PublishQueuedMessagesAsync(cts.Token), cts.Token).Forget(_logger);
Task.Run(() => PublishQueuedMessagesAsync(cancellationToken), cancellationToken).RunInBackground(_logger);
}
private void StopPublishing()
void StopPublishing()
{
_publishingCancellationToken?.Cancel(false);
_publishingCancellationToken?.Dispose();
_publishingCancellationToken = null;
try
{
_publishingCancellationToken?.Cancel(false);
}
finally
{
_publishingCancellationToken?.Dispose();
_publishingCancellationToken = null;
}
}
private void StopMaintainingConnection()
void StopMaintainingConnection()
{
_connectionCancellationToken?.Cancel(false);
_connectionCancellationToken?.Dispose();
_connectionCancellationToken = null;
try
{
_connectionCancellationToken?.Cancel(false);
}
finally
{
_connectionCancellationToken?.Dispose();
_connectionCancellationToken = null;
}
}
private TimeSpan GetRemainingTime(DateTime endTime)
static TimeSpan GetRemainingTime(DateTime endTime)
{
var remainingTime = endTime - DateTime.UtcNow;
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
@@ -1,5 +1,4 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
@@ -1,5 +1,4 @@
using System;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Server;
@@ -7,8 +6,8 @@ namespace MQTTnet.Extensions.ManagedClient
{
public class ManagedMqttClientOptionsBuilder
{
private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
private MqttClientOptionsBuilder _clientOptionsBuilder;
readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
MqttClientOptionsBuilder _clientOptionsBuilder;
public ManagedMqttClientOptionsBuilder WithMaxPendingMessages(int value)
{
@@ -69,7 +68,7 @@ namespace MQTTnet.Extensions.ManagedClient
options(_clientOptionsBuilder);
return this;
}
public ManagedMqttClientOptions Build()
{
if (_clientOptionsBuilder != null)
@@ -0,0 +1,18 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc
{
public interface IMqttRpcClient : IDisposable
{
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel);
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken);
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel);
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken);
}
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
<Product />
+6 -11
View File
@@ -11,12 +11,12 @@ using System.Threading.Tasks;
namespace MQTTnet.Extensions.Rpc
{
public sealed class MqttRpcClient : IDisposable
public sealed class MqttRpcClient : IMqttRpcClient
{
private readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
private readonly IMqttClient _mqttClient;
private readonly IMqttRpcClientOptions _options;
private readonly RpcAwareApplicationMessageReceivedHandler _applicationMessageReceivedHandler;
readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
readonly IMqttClient _mqttClient;
readonly IMqttRpcClientOptions _options;
readonly RpcAwareApplicationMessageReceivedHandler _applicationMessageReceivedHandler;
[Obsolete("Use MqttRpcClient(IMqttClient mqttClient, IMqttRpcClientOptions options).")]
public MqttRpcClient(IMqttClient mqttClient) : this(mqttClient, new MqttRpcClientOptions())
@@ -54,11 +54,6 @@ namespace MQTTnet.Extensions.Rpc
{
if (methodName == null) throw new ArgumentNullException(nameof(methodName));
if (methodName.Contains("/") || methodName.Contains("+") || methodName.Contains("#"))
{
throw new ArgumentException("The method name cannot contain /, + or #.");
}
if (!(_mqttClient.ApplicationMessageReceivedHandler is RpcAwareApplicationMessageReceivedHandler))
{
throw new InvalidOperationException("The application message received handler was modified.");
@@ -139,7 +134,7 @@ namespace MQTTnet.Extensions.Rpc
}
}
private Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var tcs))
{
@@ -2,7 +2,7 @@
namespace MQTTnet.Extensions.Rpc.Options
{
public class MqttRpcClientOptions : IMqttRpcClientOptions
public sealed class MqttRpcClientOptions : IMqttRpcClientOptions
{
public IMqttRpcClientTopicGenerationStrategy TopicGenerationStrategy { get; set; } = new DefaultMqttRpcClientTopicGenerationStrategy();
}
@@ -3,7 +3,7 @@ using System;
namespace MQTTnet.Extensions.Rpc.Options
{
public class MqttRpcClientOptionsBuilder
public sealed class MqttRpcClientOptionsBuilder
{
IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy();
@@ -1,6 +1,6 @@
namespace MQTTnet.Extensions.Rpc.Options
{
public class MqttRpcTopicPair
public sealed class MqttRpcTopicPair
{
public string RequestTopic { get; set; }
@@ -2,10 +2,15 @@
namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
{
public class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
public sealed class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
{
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
{
if (context.MethodName.Contains("/") || context.MethodName.Contains("+") || context.MethodName.Contains("#"))
{
throw new ArgumentException("The method name cannot contain /, + or #.");
}
var requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{context.MethodName}";
var responseTopic = requestTopic + "/response";
@@ -3,10 +3,18 @@ using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
{
public class TopicGenerationContext
public sealed class TopicGenerationContext
{
public string MethodName { get; set; }
/// <summary>
/// Gets or sets the quality of service level.
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message.
/// There are 3 QoS levels in MQTT:
/// - At most once (0): Message gets delivered no time, once or multiple times.
/// - At least once (1): Message gets delivered at least once (one time or more often).
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
/// </summary>
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
public IMqttClient MqttClient { get; set; }
@@ -4,9 +4,9 @@ using MQTTnet.Client.Receiving;
namespace MQTTnet.Extensions.Rpc
{
public class RpcAwareApplicationMessageReceivedHandler : IMqttApplicationMessageReceivedHandler
public sealed class RpcAwareApplicationMessageReceivedHandler : IMqttApplicationMessageReceivedHandler
{
private readonly Func<MqttApplicationMessageReceivedEventArgs, Task> _handleReceivedApplicationMessageAsync;
readonly Func<MqttApplicationMessageReceivedEventArgs, Task> _handleReceivedApplicationMessageAsync;
public RpcAwareApplicationMessageReceivedHandler(
IMqttApplicationMessageReceivedHandler originalHandler,
@@ -18,14 +18,14 @@ namespace MQTTnet.Extensions.Rpc
public IMqttApplicationMessageReceivedHandler OriginalHandler { get; }
public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{
if (OriginalHandler != null)
{
await OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
return OriginalHandler.HandleApplicationMessageReceivedAsync(eventArgs);
}
await _handleReceivedApplicationMessageAsync(eventArgs).ConfigureAwait(false);
return _handleReceivedApplicationMessageAsync(eventArgs);
}
}
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
<Product />
@@ -16,14 +16,14 @@ using WebSocket4Net;
namespace MQTTnet.Extensions.WebSocket4Net
{
public class WebSocket4NetMqttChannel : IMqttChannel
public sealed class WebSocket4NetMqttChannel : IMqttChannel
{
private readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
private readonly IMqttClientOptions _clientOptions;
private readonly MqttClientWebSocketOptions _webSocketOptions;
readonly IMqttClientOptions _clientOptions;
readonly MqttClientWebSocketOptions _webSocketOptions;
private WebSocket _webSocket;
WebSocket _webSocket;
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions)
{
@@ -166,12 +166,12 @@ namespace MQTTnet.Extensions.WebSocket4Net
_webSocket = null;
}
private void OnError(object sender, ErrorEventArgs e)
static void OnError(object sender, ErrorEventArgs e)
{
System.Diagnostics.Debug.Write(e.Exception.ToString());
}
private void OnDataReceived(object sender, DataReceivedEventArgs e)
void OnDataReceived(object sender, DataReceivedEventArgs e)
{
foreach (var @byte in e.Data)
{
@@ -179,7 +179,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
}
}
private async Task ConnectInternalAsync(CancellationToken cancellationToken)
async Task ConnectInternalAsync(CancellationToken cancellationToken)
{
_webSocket.Error += OnError;
_webSocket.DataReceived += OnDataReceived;
@@ -201,7 +201,9 @@ namespace MQTTnet.Extensions.WebSocket4Net
_webSocket.Opened += SuccessHandler;
_webSocket.Error += ErrorHandler;
#pragma warning disable AsyncFixer02 // Long-running or blocking operations inside an async method
_webSocket.Open();
#pragma warning restore AsyncFixer02 // Long-running or blocking operations inside an async method
var exception = await MqttTaskTimeout.WaitAsync(c =>
{
@@ -7,7 +7,7 @@ using System;
namespace MQTTnet.Extensions.WebSocket4Net
{
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
public sealed class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
{
readonly IMqttNetLogger _logger;
@@ -23,14 +23,22 @@ 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),
options.PacketInspector,
_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),
options.PacketInspector,
_logger);
}
default:
{
@@ -1,134 +0,0 @@
using MQTTnet.Client;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Channel;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
using WebSocket4Net;
namespace MQTTnet.TestApp.NetCore
{
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
{
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (logger == null) throw new ArgumentNullException(nameof(logger));
if (!(options.ChannelOptions is MqttClientWebSocketOptions))
{
throw new NotSupportedException("Only WebSocket connections are supported.");
}
return new MqttChannelAdapter(new WebSocket4NetMqttChannel(options), new MqttPacketSerializer(), logger);
}
private class WebSocket4NetMqttChannel : IMqttChannel
{
private readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
private readonly IMqttClientOptions _clientOptions;
private WebSocket4Net.WebSocket _webSocket;
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions)
{
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
}
public string Endpoint { get; } = "";
public Task ConnectAsync(CancellationToken cancellationToken)
{
var channelOptions = (MqttClientWebSocketOptions)_clientOptions.ChannelOptions;
var uri = "ws://" + channelOptions.Uri;
var sslProtocols = SslProtocols.None;
if (channelOptions.TlsOptions.UseTls)
{
uri = "wss://" + channelOptions.Uri;
sslProtocols = SslProtocols.Tls12;
}
var subProtocol = channelOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
_webSocket = new WebSocket4Net.WebSocket(uri, subProtocol, sslProtocols: sslProtocols);
_webSocket.DataReceived += OnDataReceived;
_webSocket.Open();
SpinWait.SpinUntil(() => _webSocket.State == WebSocketState.Open, _clientOptions.CommunicationTimeout);
return Task.FromResult(0);
}
public Task DisconnectAsync()
{
if (_webSocket != null)
{
_webSocket.DataReceived -= OnDataReceived;
_webSocket.Close();
SpinWait.SpinUntil(() => _webSocket.State == WebSocketState.Closed, _clientOptions.CommunicationTimeout);
}
_webSocket = null;
return Task.FromResult(0);
}
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
var readBytes = 0;
while (count > 0 && !cancellationToken.IsCancellationRequested)
{
byte @byte;
if (readBytes == 0)
{
// Block until at lease one byte was received.
@byte = _receiveBuffer.Take(cancellationToken);
}
else
{
if (!_receiveBuffer.TryTake(out @byte))
{
return Task.FromResult(readBytes);
}
}
buffer[offset] = @byte;
offset++;
count--;
readBytes++;
}
return Task.FromResult(readBytes);
}
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
_webSocket.Send(buffer, offset, count);
return Task.FromResult(0);
}
public void Dispose()
{
if (_webSocket != null)
{
_webSocket.DataReceived -= OnDataReceived;
_webSocket.Dispose();
}
}
private void OnDataReceived(object sender, WebSocket4Net.DataReceivedEventArgs e)
{
foreach (var @byte in e.Data)
{
_receiveBuffer.Add(@byte);
}
}
}
}
}
+13 -6
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFrameworks>net5.0</TargetFrameworks>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<AssemblyName>MQTTnet.Server</AssemblyName>
<RootNamespace>MQTTnet.Server</RootNamespace>
@@ -39,12 +39,19 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="IronPython" Version="2.7.10" />
<PackageReference Include="IronPython.StdLib" Version="2.7.10" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.5" />
<PackageReference Include="IronPython" Version="2.7.11" />
<PackageReference Include="IronPython.StdLib" Version="2.7.11" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.5.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.5.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.6.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.6.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net5.0'">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.5" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
@@ -212,9 +212,15 @@ namespace MQTTnet.Server.Mqtt
// Configure encrypted connections
if (_settings.EncryptedTcpEndPoint.Enabled)
{
#if NETCOREAPP3_1 || NET5_0
options
.WithEncryptedEndpoint()
.WithEncryptionSslProtocol(SslProtocols.Tls13);
#else
options
.WithEncryptedEndpoint()
.WithEncryptionSslProtocol(SslProtocols.Tls12);
#endif
if (!string.IsNullOrEmpty(_settings.EncryptedTcpEndPoint?.Certificate?.Path))
{
+8 -4
View File
@@ -116,10 +116,15 @@ namespace MQTTnet.Server.Web
{
var securityScheme = new OpenApiSecurityScheme
{
Scheme = "Basic",
Scheme = "basic",
Name = HeaderNames.Authorization,
Type = SecuritySchemeType.Http,
In = ParameterLocation.Header
In = ParameterLocation.Header,
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Swagger"
}
};
c.AddSecurityDefinition("Swagger", securityScheme);
@@ -181,8 +186,7 @@ namespace MQTTnet.Server.Web
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromSeconds(mqttSettings.WebSocketEndPoint.KeepAliveInterval),
ReceiveBufferSize = mqttSettings.WebSocketEndPoint.ReceiveBufferSize
KeepAliveInterval = TimeSpan.FromSeconds(mqttSettings.WebSocketEndPoint.KeepAliveInterval)
};
if (mqttSettings.WebSocketEndPoint.AllowedOrigins?.Any() == true)
@@ -21,17 +21,15 @@ namespace MQTTnet.Adapter
long BytesReceived { get; }
Action ReadingPacketStartedCallback { get; set; }
Action ReadingPacketCompletedCallback { get; set; }
bool IsReadingPacket { get; }
Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken);
Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken);
Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken);
Task<MqttBasePacket> ReceivePacketAsync(CancellationToken cancellationToken);
void ResetStatistics();
}
+189 -95
View File
@@ -11,6 +11,7 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Adapter
{
@@ -19,26 +20,26 @@ namespace MQTTnet.Adapter
const uint ErrorOperationAborted = 0x800703E3;
const int ReadBufferSize = 4096;
readonly byte[] _singleByteBuffer = new byte[1];
readonly byte[] _fixedHeaderBuffer = new byte[2];
readonly MqttPacketInspectorHandler _packetInspectorHandler;
readonly IMqttNetScopedLogger _logger;
readonly IMqttChannel _channel;
readonly MqttPacketReader _packetReader;
readonly byte[] _fixedHeaderBuffer = new byte[2];
readonly AsyncLock _syncRoot = new AsyncLock();
long _bytesReceived;
long _bytesSent;
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetLogger logger)
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttPacketInspector packetInspector, IMqttNetLogger logger)
{
if (logger == null) throw new ArgumentNullException(nameof(logger));
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
_packetReader = new MqttPacketReader(_channel);
_packetInspectorHandler = new MqttPacketInspectorHandler(packetInspector, logger);
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateScopedLogger(nameof(MqttChannelAdapter));
}
@@ -51,10 +52,10 @@ namespace MQTTnet.Adapter
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public long BytesSent => Interlocked.Read(ref _bytesSent);
public long BytesReceived => Interlocked.Read(ref _bytesReceived);
public Action ReadingPacketStartedCallback { get; set; }
public Action ReadingPacketCompletedCallback { get; set; }
public bool IsReadingPacket { get; private set; }
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
@@ -79,7 +80,7 @@ namespace MQTTnet.Adapter
throw;
}
WrapException(exception);
WrapAndThrowException(exception);
}
}
@@ -107,97 +108,78 @@ namespace MQTTnet.Adapter
throw;
}
WrapException(exception);
WrapAndThrowException(exception);
}
}
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false))
{
// Check for cancellation here again because "WaitAsync" might take some time.
cancellationToken.ThrowIfCancellationRequested();
try
{
var packetData = PacketFormatterAdapter.Encode(packet);
_packetInspectorHandler.BeginSendPacket(packetData);
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
Interlocked.Add(ref _bytesReceived, packetData.Count);
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
}
catch (Exception exception)
{
if (IsWrappedException(exception))
{
throw;
}
WrapAndThrowException(exception);
}
finally
{
PacketFormatterAdapter.FreeBuffer();
}
}
}
public async Task<MqttBasePacket> ReceivePacketAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
try
{
using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false))
{
// Check for cancellation here again because "WaitAsync" might take some time.
cancellationToken.ThrowIfCancellationRequested();
_packetInspectorHandler.BeginReceivePacket();
try
{
var packetData = PacketFormatterAdapter.Encode(packet);
if (timeout == TimeSpan.Zero)
{
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
}
else
{
await MqttTaskTimeout.WaitAsync(
t => _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, t), timeout, cancellationToken).ConfigureAwait(false);
}
Interlocked.Add(ref _bytesReceived, packetData.Count);
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
}
catch (Exception exception)
{
if (IsWrappedException(exception))
{
throw;
}
WrapException(exception);
}
finally
{
PacketFormatterAdapter.FreeBuffer();
}
}
}
catch (ObjectDisposedException)
{
throw new OperationCanceledException();
}
}
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
try
{
ReceivedMqttPacket receivedMqttPacket;
if (timeout == TimeSpan.Zero)
{
receivedMqttPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
}
else
{
receivedMqttPacket = await MqttTaskTimeout.WaitAsync(ReceiveAsync, timeout, cancellationToken).ConfigureAwait(false);
}
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
var receivedPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
if (receivedPacket == null || cancellationToken.IsCancellationRequested)
{
return null;
}
Interlocked.Add(ref _bytesSent, receivedMqttPacket.TotalLength);
_packetInspectorHandler.EndReceivePacket();
Interlocked.Add(ref _bytesSent, receivedPacket.TotalLength);
if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown)
{
PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket);
PacketFormatterAdapter.DetectProtocolVersion(receivedPacket);
}
var packet = PacketFormatterAdapter.Decode(receivedMqttPacket);
var packet = PacketFormatterAdapter.Decode(receivedPacket);
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
}
_logger.Verbose("RX ({0} bytes) <<< {1}", receivedMqttPacket.TotalLength, packet);
_logger.Verbose("RX ({0} bytes) <<< {1}", receivedPacket.TotalLength, packet);
return packet;
}
@@ -214,7 +196,7 @@ namespace MQTTnet.Adapter
throw;
}
WrapException(exception);
WrapAndThrowException(exception);
}
return null;
@@ -239,31 +221,38 @@ namespace MQTTnet.Adapter
async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
{
var readFixedHeaderResult = await _packetReader.ReadFixedHeaderAsync(_fixedHeaderBuffer, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
var readFixedHeaderResult = await ReadFixedHeaderAsync(cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
if (readFixedHeaderResult.ConnectionClosed)
{
return null;
}
try
{
if (readFixedHeaderResult.ConnectionClosed)
{
return null;
}
ReadingPacketStartedCallback?.Invoke();
IsReadingPacket = true;
var fixedHeader = readFixedHeaderResult.FixedHeader;
if (fixedHeader.RemainingLength == 0)
{
return new ReceivedMqttPacket(fixedHeader.Flags, null, 2);
return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(new byte[0], 0, 0), 2);
}
var body = new byte[fixedHeader.RemainingLength];
var bodyLength = fixedHeader.RemainingLength;
var body = new byte[bodyLength];
var bodyOffset = 0;
var chunkSize = Math.Min(ReadBufferSize, fixedHeader.RemainingLength);
var chunkSize = Math.Min(ReadBufferSize, bodyLength);
do
{
@@ -286,25 +275,126 @@ namespace MQTTnet.Adapter
}
bodyOffset += readBytes;
} while (bodyOffset < body.Length);
} while (bodyOffset < bodyLength);
var bodyReader = new MqttPacketBodyReader(body, 0, body.Length);
_packetInspectorHandler.FillReceiveBuffer(body);
var bodyReader = new MqttPacketBodyReader(body, 0, bodyLength);
return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength);
}
finally
{
ReadingPacketCompletedCallback?.Invoke();
IsReadingPacket = false;
}
}
async Task<ReadFixedHeaderResult> ReadFixedHeaderAsync(CancellationToken cancellationToken)
{
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
var buffer = _fixedHeaderBuffer;
var totalBytesRead = 0;
while (totalBytesRead < buffer.Length)
{
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
if (bytesRead == 0)
{
return new ReadFixedHeaderResult
{
ConnectionClosed = true
};
}
totalBytesRead += bytesRead;
}
_packetInspectorHandler.FillReceiveBuffer(buffer);
var hasRemainingLength = buffer[1] != 0;
if (!hasRemainingLength)
{
return new ReadFixedHeaderResult
{
FixedHeader = new MqttFixedHeader(buffer[0], 0, totalBytesRead)
};
}
var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false);
if (!bodyLength.HasValue)
{
return new ReadFixedHeaderResult
{
ConnectionClosed = true
};
}
totalBytesRead += bodyLength.Value;
return new ReadFixedHeaderResult
{
FixedHeader = new MqttFixedHeader(buffer[0], bodyLength.Value, totalBytesRead)
};
}
async Task<int?> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
{
var offset = 0;
var multiplier = 128;
var value = initialEncodedByte & 127;
int encodedByte = initialEncodedByte;
while ((encodedByte & 128) != 0)
{
offset++;
if (offset > 3)
{
throw new MqttProtocolViolationException("Remaining length is invalid.");
}
if (cancellationToken.IsCancellationRequested)
{
return null;
}
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
if (readCount == 0)
{
return null;
}
_packetInspectorHandler.FillReceiveBuffer(_singleByteBuffer);
encodedByte = _singleByteBuffer[0];
value += (encodedByte & 127) * multiplier;
multiplier *= 128;
}
return value;
}
static bool IsWrappedException(Exception exception)
{
return exception is OperationCanceledException ||
exception is MqttCommunicationTimedOutException ||
exception is MqttCommunicationException;
exception is MqttCommunicationException ||
exception is MqttProtocolViolationException;
}
static void WrapException(Exception exception)
static void WrapAndThrowException(Exception exception)
{
if (exception is IOException && exception.InnerException is SocketException innerException)
{
@@ -313,11 +403,15 @@ namespace MQTTnet.Adapter
if (exception is SocketException socketException)
{
if (socketException.SocketErrorCode == SocketError.ConnectionAborted ||
socketException.SocketErrorCode == SocketError.OperationAborted)
if (socketException.SocketErrorCode == SocketError.OperationAborted)
{
throw new OperationCanceledException();
}
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
{
throw new MqttCommunicationException(socketException);
}
}
if (exception is COMException comException)
@@ -0,0 +1,84 @@
using System;
using System.IO;
using System.Linq;
using MQTTnet.Diagnostics;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Adapter
{
public sealed class MqttPacketInspectorHandler
{
readonly MemoryStream _receivedPacketBuffer;
readonly IMqttPacketInspector _packetInspector;
readonly IMqttNetScopedLogger _logger;
public MqttPacketInspectorHandler(IMqttPacketInspector packetInspector, IMqttNetLogger logger)
{
_packetInspector = packetInspector;
if (packetInspector != null)
{
_receivedPacketBuffer = new MemoryStream();
}
if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateScopedLogger(nameof(MqttPacketInspectorHandler));
}
public void BeginReceivePacket()
{
_receivedPacketBuffer?.SetLength(0);
}
public void EndReceivePacket()
{
if (_packetInspector == null)
{
return;
}
var buffer = _receivedPacketBuffer.ToArray();
_receivedPacketBuffer.SetLength(0);
InspectPacket(buffer, MqttPacketFlowDirection.Inbound);
}
public void BeginSendPacket(ArraySegment<byte> buffer)
{
if (_packetInspector == null)
{
return;
}
// Create a copy of the actual packet so that the inspector gets no access
// to the internal buffers. This is waste of memory but this feature is only
// intended for debugging etc. so that this is OK.
var bufferCopy = buffer.ToArray();
InspectPacket(bufferCopy, MqttPacketFlowDirection.Outbound);
}
public void FillReceiveBuffer(byte[] buffer)
{
_receivedPacketBuffer?.Write(buffer, 0, buffer.Length);
}
void InspectPacket(byte[] buffer, MqttPacketFlowDirection direction)
{
try
{
var context = new ProcessMqttPacketContext
{
Buffer = buffer,
Direction = direction
};
_packetInspector.ProcessMqttPacket(context);
}
catch (Exception exception)
{
_logger.Error(exception, "Error while inspecting packet.");
}
}
}
}
+7 -6
View File
@@ -1,19 +1,20 @@
using MQTTnet.Formatter;
using System;
using MQTTnet.Formatter;
namespace MQTTnet.Adapter
{
public class ReceivedMqttPacket
public sealed class ReceivedMqttPacket
{
public ReceivedMqttPacket(byte fixedHeader, IMqttPacketBodyReader body, int totalLength)
public ReceivedMqttPacket(byte fixedHeader, IMqttPacketBodyReader bodyReader, int totalLength)
{
FixedHeader = fixedHeader;
Body = body;
BodyReader = bodyReader ?? throw new ArgumentNullException(nameof(bodyReader));
TotalLength = totalLength;
}
public byte FixedHeader { get; set; }
public byte FixedHeader { get; }
public IMqttPacketBodyReader Body { get; }
public IMqttPacketBodyReader BodyReader { get; }
public int TotalLength { get; }
}
@@ -1,11 +1,16 @@
using System.Collections.Generic;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Client.Connecting
{
// TODO: Consider renaming this to _MqttClientConnectResult_
public class MqttClientAuthenticateResult
{
/// <summary>
/// Gets or sets the result code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientConnectResultCode ResultCode { get; set; }
public bool IsSessionPresent { get; set; }
@@ -16,16 +21,34 @@ namespace MQTTnet.Client.Connecting
public string AssignedClientIdentifier { get; set; }
/// <summary>
/// Gets or sets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; set; }
/// <summary>
/// Gets or sets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; set; }
public uint? MaximumPacketSize { get; set; }
/// <summary>
/// Gets or sets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; set; }
public ushort? ReceiveMaximum { get; set; }
public MqttQualityOfServiceLevel MaximumQoS { get; set; }
/// <summary>
/// Gets or sets the response information.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ResponseInformation { get; set; }
public ushort? TopicAliasMaximum { get; set; }
@@ -36,10 +59,25 @@ namespace MQTTnet.Client.Connecting
public uint? SessionExpiryInterval { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the subscription identifiers are available or not.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? SubscriptionIdentifiersAvailable { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the shared subscriptions are available or not.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? SharedSubscriptionAvailable { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; }
}
}
@@ -9,6 +9,10 @@ namespace MQTTnet.Client.Connecting
AuthenticateResult = authenticateResult ?? throw new ArgumentNullException(nameof(authenticateResult));
}
/// <summary>
/// Gets the authentication result.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientAuthenticateResult AuthenticateResult { get; }
}
}
@@ -1,9 +1,17 @@
namespace MQTTnet.Client.Disconnecting
{
public class MqttClientDisconnectOptions
public sealed class MqttClientDisconnectOptions
{
/// <summary>
/// Gets or sets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
/// <summary>
/// Gets or sets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; set; }
}
}
@@ -3,22 +3,35 @@ using MQTTnet.Client.Connecting;
namespace MQTTnet.Client.Disconnecting
{
public class MqttClientDisconnectedEventArgs : EventArgs
public sealed class MqttClientDisconnectedEventArgs : EventArgs
{
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientAuthenticateResult authenticateResult, MqttClientDisconnectReason reasonCode)
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientAuthenticateResult authenticateResult, MqttClientDisconnectReason reason)
{
ClientWasConnected = clientWasConnected;
Exception = exception;
AuthenticateResult = authenticateResult;
ReasonCode = reasonCode;
Reason = reason;
ReasonCode = reason;
}
public bool ClientWasConnected { get; }
public Exception Exception { get; }
/// <summary>
/// Gets the authentication result.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientAuthenticateResult AuthenticateResult { get; }
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
/// <summary>
/// Gets or sets the reason.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientDisconnectReason Reason { get; set; }
[Obsolete("Please use 'Reason' instead. This property will be removed in the future!")]
public MqttClientDisconnectReason ReasonCode { get; set; }
}
}
@@ -20,14 +20,37 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange
Client = client ?? throw new ArgumentNullException(nameof(client));
}
/// <summary>
/// Gets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttAuthenticateReasonCode ReasonCode { get; }
/// <summary>
/// Gets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; }
/// <summary>
/// Gets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; }
/// <summary>
/// Gets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; }
/// <summary>
/// Gets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; }
public IMqttClient Client { get; }
@@ -6,12 +6,33 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange
{
public class MqttExtendedAuthenticationExchangeData
{
/// <summary>
/// Gets or sets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttAuthenticateReasonCode ReasonCode { get; set; }
/// <summary>
/// Gets or sets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; set; }
/// <summary>
/// Gets or sets the authentication data.
/// Authentication data is binary information used to transmit multiple iterations of cryptographic secrets of protocol steps.
/// The content of the authentication data is highly dependent on the specific implementation of the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; }
}
}
+20
View File
@@ -13,18 +13,38 @@ namespace MQTTnet.Client
public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
{
bool IsConnected { get; }
IMqttClientOptions Options { get; }
/// <summary>
/// Gets or sets the connected handler that is fired after the client has connected to the server successfully.
/// Hint: Initialize handlers before you connect the client to avoid issues.
/// </summary>
IMqttClientConnectedHandler ConnectedHandler { get; set; }
/// <summary>
/// Gets or sets the disconnected handler that is fired after the client has disconnected from the server.
/// Hint: Initialize handlers before you connect the client to avoid issues.
/// </summary>
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
Task<MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken);
Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken);
Task PingAsync(CancellationToken cancellationToken);
/// <summary>
/// Sends extended authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
/// <param name="data">The extended data.</param>
/// <param name="cancellationToken">A cancellation token to stop the task.</param>
/// <returns>A <see cref="Task"/> representing any asynchronous operation.</returns>
Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken);
Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken);
Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken);
}
}
+1 -2
View File
@@ -1,5 +1,4 @@
using System;
using MQTTnet.Adapter;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.LowLevelClient;
+122 -71
View File
@@ -17,6 +17,7 @@ using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Implementations;
namespace MQTTnet.Client
{
@@ -42,6 +43,7 @@ namespace MQTTnet.Client
bool _cleanDisconnectInitiated;
long _isDisconnectPending;
bool _isConnected;
MqttClientDisconnectReason _disconnectReason;
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
{
@@ -77,7 +79,7 @@ namespace MQTTnet.Client
Options = options;
_packetIdentifierProvider.Reset();
_packetDispatcher.Reset();
_packetDispatcher.Cancel();
_backgroundCancellationTokenSource = new CancellationTokenSource();
var backgroundCancellationToken = _backgroundCancellationTokenSource.Token;
@@ -88,8 +90,8 @@ namespace MQTTnet.Client
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("Trying to connect with server '{0}' (Timeout={1}).", options.ChannelOptions, options.CommunicationTimeout);
await adapter.ConnectAsync(options.CommunicationTimeout, combined.Token).ConfigureAwait(false);
_logger.Verbose("Connection with server established.");
_publishPacketReceiverQueue = new AsyncQueue<MqttPublishPacket>();
@@ -122,6 +124,8 @@ namespace MQTTnet.Client
}
catch (Exception exception)
{
_disconnectReason = MqttClientDisconnectReason.UnspecifiedError;
_logger.Error(exception, "Error while connecting with server.");
if (!DisconnectIsPending())
@@ -139,11 +143,17 @@ namespace MQTTnet.Client
ThrowIfDisposed();
if (DisconnectIsPending())
{
return;
}
try
{
_disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
_cleanDisconnectInitiated = true;
if (IsConnected)
if (_isConnected)
{
var disconnectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(options);
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
@@ -151,10 +161,7 @@ namespace MQTTnet.Client
}
finally
{
if (!DisconnectIsPending())
{
await DisconnectInternalAsync(null, null, null).ConfigureAwait(false);
}
await DisconnectInternalAsync(null, null, null).ConfigureAwait(false);
}
}
@@ -213,9 +220,9 @@ namespace MQTTnet.Client
public Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
cancellationToken.ThrowIfCancellationRequested();
MqttTopicValidator.ThrowIfInvalid(applicationMessage.Topic);
MqttTopicValidator.ThrowIfInvalid(applicationMessage);
ThrowIfDisposed();
ThrowIfNotConnected();
@@ -245,24 +252,27 @@ namespace MQTTnet.Client
void Cleanup()
{
_backgroundCancellationTokenSource?.Cancel(false);
_backgroundCancellationTokenSource?.Dispose();
_backgroundCancellationTokenSource = null;
try
{
_backgroundCancellationTokenSource?.Cancel(false);
}
finally
{
_backgroundCancellationTokenSource?.Dispose();
_backgroundCancellationTokenSource = null;
_publishPacketReceiverQueue?.Dispose();
_publishPacketReceiverQueue = null;
_publishPacketReceiverQueue?.Dispose();
_publishPacketReceiverQueue = null;
_adapter?.Dispose();
_adapter?.Dispose();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Cleanup();
DisconnectedHandler = null;
}
base.Dispose(disposing);
@@ -302,8 +312,7 @@ namespace MQTTnet.Client
async Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult)
{
var clientWasConnected = IsConnected;
var reasonCode = MqttClientDisconnectReason.NormalDisconnection;
var clientWasConnected = _isConnected;
TryInitiateDisconnect();
_isConnected = false;
@@ -321,7 +330,6 @@ namespace MQTTnet.Client
catch (Exception adapterException)
{
_logger.Warning(adapterException, "Error while disconnecting from adapter.");
reasonCode = MqttClientDisconnectReason.UnspecifiedError;
}
try
@@ -337,7 +345,6 @@ namespace MQTTnet.Client
catch (Exception e)
{
_logger.Warning(e, "Error while waiting for internal tasks.");
reasonCode = MqttClientDisconnectReason.UnspecifiedError;
}
finally
{
@@ -351,7 +358,7 @@ namespace MQTTnet.Client
{
// This handler must be executed in a new thread because otherwise a dead lock may happen
// when trying to reconnect in that handler etc.
Task.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, authenticateResult, reasonCode))).Forget(_logger);
Task.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, authenticateResult, _disconnectReason))).RunInBackground(_logger);
}
}
}
@@ -362,11 +369,6 @@ namespace MQTTnet.Client
{
try
{
if (_backgroundCancellationTokenSource?.IsCancellationRequested == true)
{
return;
}
_backgroundCancellationTokenSource?.Cancel(false);
}
catch (Exception exception)
@@ -382,17 +384,17 @@ namespace MQTTnet.Client
_sendTracker.Restart();
return _adapter.SendPacketAsync(packet, Options.CommunicationTimeout, cancellationToken);
return _adapter.SendPacketAsync(packet, cancellationToken);
}
async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttBasePacket
{
cancellationToken.ThrowIfCancellationRequested();
ushort identifier = 0;
if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier.HasValue)
if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier && packetWithIdentifier.PacketIdentifier > 0)
{
identifier = packetWithIdentifier.PacketIdentifier.Value;
identifier = packetWithIdentifier.PacketIdentifier;
}
using (var packetAwaiter = _packetDispatcher.AddAwaiter<TResponsePacket>(identifier))
@@ -400,12 +402,11 @@ namespace MQTTnet.Client
try
{
_sendTracker.Restart();
await _adapter.SendPacketAsync(requestPacket, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
_logger.Warning(e, "Error when sending packet of type '{0}'.", typeof(TResponsePacket).Name);
_logger.Warning(e, "Error when sending request packet ({0}).", requestPacket.GetType().Name);
packetAwaiter.Cancel();
}
@@ -417,7 +418,7 @@ namespace MQTTnet.Client
{
if (exception is MqttCommunicationTimedOutException)
{
_logger.Warning(null, "Timeout while waiting for packet of type '{0}'.", typeof(TResponsePacket).Name);
_logger.Warning(null, "Timeout while waiting for response packet ({0}).", typeof(TResponsePacket).Name);
}
throw;
@@ -437,10 +438,10 @@ namespace MQTTnet.Client
{
// Values described here: [MQTT-3.1.2-24].
var waitTime = keepAlivePeriod - _sendTracker.Elapsed;
if (waitTime <= TimeSpan.Zero)
{
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket(), cancellationToken).ConfigureAwait(false);
await SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken).ConfigureAwait(false);
}
// Wait a fixed time in all cases. Calculation of the remaining time is complicated
@@ -459,6 +460,7 @@ namespace MQTTnet.Client
if (exception is OperationCanceledException)
{
return;
}
else if (exception is MqttCommunicationException)
{
@@ -488,7 +490,7 @@ namespace MQTTnet.Client
while (!cancellationToken.IsCancellationRequested)
{
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
var packet = await _adapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
@@ -550,32 +552,25 @@ namespace MQTTnet.Client
{
EnqueueReceivedPublishPacket(publishPacket);
}
else if (packet is MqttPubRecPacket pubRecPacket)
{
await ProcessReceivedPubRecPacket(pubRecPacket, cancellationToken).ConfigureAwait(false);
}
else if (packet is MqttPubRelPacket pubRelPacket)
{
await SendAsync(new MqttPubCompPacket
{
PacketIdentifier = pubRelPacket.PacketIdentifier,
ReasonCode = MqttPubCompReasonCode.Success
}, cancellationToken).ConfigureAwait(false);
await ProcessReceivedPubRelPacket(pubRelPacket, cancellationToken).ConfigureAwait(false);
}
else if (packet is MqttPingReqPacket)
{
await SendAsync(new MqttPingRespPacket(), cancellationToken).ConfigureAwait(false);
await SendAsync(MqttPingRespPacket.Instance, cancellationToken).ConfigureAwait(false);
}
else if (packet is MqttDisconnectPacket)
else if (packet is MqttDisconnectPacket disconnectPacket)
{
// Also dispatch disconnect to waiting threads to generate a proper exception.
_packetDispatcher.Dispatch(packet);
await DisconnectAsync(null, cancellationToken).ConfigureAwait(false);
await ProcessReceivedDisconnectPacket(disconnectPacket).ConfigureAwait(false);
}
else if (packet is MqttAuthPacket authPacket)
{
var extendedAuthenticationExchangeHandler = Options.ExtendedAuthenticationExchangeHandler;
if (extendedAuthenticationExchangeHandler != null)
{
await extendedAuthenticationExchangeHandler.HandleRequestAsync(new MqttExtendedAuthenticationExchangeContext(authPacket, this)).ConfigureAwait(false);
}
await ProcessReceivedAuthPacket(authPacket).ConfigureAwait(false);
}
else
{
@@ -618,7 +613,7 @@ namespace MQTTnet.Client
}
catch (Exception exception)
{
_logger.Error(exception, "Error while enqueueing application message.");
_logger.Error(exception, "Error while queueing application message.");
}
}
@@ -628,8 +623,7 @@ namespace MQTTnet.Client
{
try
{
var publishPacketDequeueResult = await _publishPacketReceiverQueue.TryDequeueAsync(cancellationToken);
var publishPacketDequeueResult = await _publishPacketReceiverQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);
if (!publishPacketDequeueResult.IsSuccess)
{
return;
@@ -643,26 +637,28 @@ namespace MQTTnet.Client
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
if (await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false))
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (!eventArgs.ProcessingFailed)
{
await SendAsync(new MqttPubAckPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubAckReasonCode.Success
ReasonCode = (MqttPubAckReasonCode)eventArgs.ReasonCode
}, cancellationToken).ConfigureAwait(false);
}
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
if (await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false))
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
if (!eventArgs.ProcessingFailed)
{
var pubRecPacket = new MqttPubRecPacket
await SendAsync(new MqttPubRecPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRecReasonCode.Success
};
await SendAsync(pubRecPacket, cancellationToken).ConfigureAwait(false);
ReasonCode = (MqttPubRecReasonCode)eventArgs.ReasonCode
}, cancellationToken).ConfigureAwait(false);
}
}
else
@@ -670,6 +666,9 @@ namespace MQTTnet.Client
throw new MqttProtocolViolationException("Received a not supported QoS level.");
}
}
catch (OperationCanceledException)
{
}
catch (Exception exception)
{
_logger.Error(exception, "Error while handling application message.");
@@ -677,6 +676,57 @@ namespace MQTTnet.Client
}
}
Task ProcessReceivedPubRecPacket(MqttPubRecPacket pubRecPacket, CancellationToken cancellationToken)
{
if (!_packetDispatcher.TryDispatch(pubRecPacket))
{
// The packet is unknown. Probably due to a restart of the client.
// So wen send this to the server to trigger a full resend of the message.
return SendAsync(new MqttPubRelPacket
{
PacketIdentifier = pubRecPacket.PacketIdentifier,
ReasonCode = MqttPubRelReasonCode.PacketIdentifierNotFound
}, cancellationToken);
}
return PlatformAbstractionLayer.CompletedTask;
}
Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
{
return SendAsync(new MqttPubCompPacket
{
PacketIdentifier = pubRelPacket.PacketIdentifier,
ReasonCode = MqttPubCompReasonCode.Success
}, cancellationToken);
}
Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket)
{
_disconnectReason = (MqttClientDisconnectReason)(disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection);
// Also dispatch disconnect to waiting threads to generate a proper exception.
_packetDispatcher.Dispatch(disconnectPacket);
if (!DisconnectIsPending())
{
return DisconnectInternalAsync(_packetReceiverTask, null, null);
}
return PlatformAbstractionLayer.CompletedTask;
}
Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket)
{
var extendedAuthenticationExchangeHandler = Options.ExtendedAuthenticationExchangeHandler;
if (extendedAuthenticationExchangeHandler != null)
{
return extendedAuthenticationExchangeHandler.HandleRequestAsync(new MqttExtendedAuthenticationExchangeContext(authPacket, this));
}
return PlatformAbstractionLayer.CompletedTask;
}
async Task<MqttClientPublishResult> PublishAtMostOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
@@ -708,19 +758,18 @@ namespace MQTTnet.Client
return _adapter.PacketFormatterAdapter.DataConverter.CreatePublishResult(pubRecPacket, pubCompPacket);
}
async Task<bool> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
{
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage);
var handler = ApplicationMessageReceivedHandler;
if (handler != null)
{
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage);
await handler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
return !eventArgs.ProcessingFailed;
}
return true;
return eventArgs;
}
async Task WaitForTaskAsync(Task task, Task sender)
@@ -756,6 +805,8 @@ namespace MQTTnet.Client
bool DisconnectIsPending()
{
// This will read the _isDisconnectPending and set it to "1" afterwards regardless of the value.
// So the first caller will get a "false" and all subsequent ones will get "true".
return Interlocked.CompareExchange(ref _isDisconnectPending, 1, 0) != 0;
}
}
+15 -8
View File
@@ -128,7 +128,21 @@ namespace MQTTnet.Client
{
if (client == null) throw new ArgumentNullException(nameof(client));
return client.DisconnectAsync(new MqttClientDisconnectOptions());
return client.DisconnectAsync(CancellationToken.None);
}
public static Task DisconnectAsync(this IMqttClient client, MqttClientDisconnectOptions options)
{
if (client == null) throw new ArgumentNullException(nameof(client));
return client.DisconnectAsync(options, CancellationToken.None);
}
public static Task DisconnectAsync(this IMqttClient client, CancellationToken cancellationToken)
{
if (client == null) throw new ArgumentNullException(nameof(client));
return client.DisconnectAsync(new MqttClientDisconnectOptions(), cancellationToken);
}
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, params MqttTopicFilter[] topicFilters)
@@ -176,13 +190,6 @@ namespace MQTTnet.Client
return client.ConnectAsync(options, CancellationToken.None);
}
public static Task DisconnectAsync(this IMqttClient client, MqttClientDisconnectOptions options)
{
if (client == null) throw new ArgumentNullException(nameof(client));
return client.DisconnectAsync(options, CancellationToken.None);
}
public static Task SendExtendedAuthenticationExchangeDataAsync(this IMqttClient client, MqttExtendedAuthenticationExchangeData data)
{
if (client == null) throw new ArgumentNullException(nameof(client));
@@ -1,9 +1,10 @@
namespace MQTTnet.Client
{
public class MqttPacketIdentifierProvider
public sealed class MqttPacketIdentifierProvider
{
private readonly object _syncRoot = new object();
private ushort _value;
readonly object _syncRoot = new object();
ushort _value;
public void Reset()
{
@@ -3,31 +3,112 @@ using MQTTnet.Formatter;
using MQTTnet.Packets;
using System;
using System.Collections.Generic;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Client.Options
{
public interface IMqttClientOptions
{
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
string ClientId { get; }
/// <summary>
/// Gets a value indicating whether clean sessions are used or not.
/// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a persistent connection.
/// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for the client.
/// This mode is ideal when the client only publishes messages.
/// It can also connect as a durable client using a persistent connection.
/// In this mode, the broker will store subscription information, and undelivered messages for the client.
/// </summary>
bool CleanSession { get; }
IMqttClientCredentials Credentials { get; }
IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; }
MqttProtocolVersion ProtocolVersion { get; }
IMqttClientChannelOptions ChannelOptions { get; }
TimeSpan CommunicationTimeout { get; }
/// <summary>
/// Gets the keep alive period.
/// The connection is normally left open by the client so that is can send and receive data at any time.
/// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and expect to receive a PINGRESP from the broker.
/// This message exchange confirms that the connection is open and working.
/// This period is known as the keep alive period.
/// </summary>
TimeSpan KeepAlivePeriod { get; }
/// <summary>
/// Gets the last will message.
/// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client.
/// </summary>
MqttApplicationMessage WillMessage { get; }
/// <summary>
/// Gets the will delay interval.
/// This is the time between the client disconnect and the time the will message will be sent.
/// </summary>
uint? WillDelayInterval { get; }
/// <summary>
/// Gets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
string AuthenticationMethod { get; }
/// <summary>
/// Gets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
byte[] AuthenticationData { get; }
uint? MaximumPacketSize { get; }
/// <summary>
/// Gets the receive maximum.
/// This gives the maximum length of the receive messages.
/// </summary>
ushort? ReceiveMaximum { get; }
/// <summary>
/// Gets the request problem information.
/// Hint: MQTT 5 feature only.
/// </summary>
bool? RequestProblemInformation { get; }
/// <summary>
/// Gets the request response information.
/// Hint: MQTT 5 feature only.
/// </summary>
bool? RequestResponseInformation { get; }
/// <summary>
/// Gets the session expiry interval.
/// The time after a session expires when it's not actively used.
/// </summary>
uint? SessionExpiryInterval { get; }
/// <summary>
/// Gets the topic alias maximum.
/// This gives the maximum length of the topic alias.
/// </summary>
ushort? TopicAliasMaximum { get; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
List<MqttUserProperty> UserProperties { get; set; }
IMqttPacketInspector PacketInspector { get; set; }
}
}
@@ -3,33 +3,112 @@ using MQTTnet.Formatter;
using MQTTnet.Packets;
using System;
using System.Collections.Generic;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Client.Options
{
public class MqttClientOptions : IMqttClientOptions
{
/// <summary>
/// Gets the client identifier.
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
/// </summary>
public string ClientId { get; set; } = Guid.NewGuid().ToString("N");
/// <summary>
/// Gets or sets a value indicating whether clean sessions are used or not.
/// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a persistent connection.
/// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for the client.
/// This mode is ideal when the client only publishes messages.
/// It can also connect as a durable client using a persistent connection.
/// In this mode, the broker will store subscription information, and undelivered messages for the client.
/// </summary>
public bool CleanSession { get; set; } = true;
public IMqttClientCredentials Credentials { get; set; }
public IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; set; }
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
public IMqttClientChannelOptions ChannelOptions { get; set; }
public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
/// <summary>
/// Gets or sets the keep alive period.
/// The connection is normally left open by the client so that is can send and receive data at any time.
/// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and expect to receive a PINGRESP from the broker.
/// This message exchange confirms that the connection is open and working.
/// This period is known as the keep alive period.
/// </summary>
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15);
/// <summary>
/// Gets or sets the last will message.
/// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client.
/// </summary>
public MqttApplicationMessage WillMessage { get; set; }
/// <summary>
/// Gets or sets the will delay interval.
/// This is the time between the client disconnect and the time the will message will be sent.
/// </summary>
public uint? WillDelayInterval { get; set; }
/// <summary>
/// Gets or sets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; set; }
/// <summary>
/// Gets or sets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; set; }
public uint? MaximumPacketSize { get; set; }
/// <summary>
/// Gets or sets the receive maximum.
/// This gives the maximum length of the receive messages.
/// </summary>
public ushort? ReceiveMaximum { get; set; }
/// <summary>
/// Gets or sets the request problem information.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? RequestProblemInformation { get; set; }
/// <summary>
/// Gets or sets the request response information.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? RequestResponseInformation { get; set; }
/// <summary>
/// Gets or sets the session expiry interval.
/// The time after a session expires when it's not actively used.
/// </summary>
public uint? SessionExpiryInterval { get; set; }
/// <summary>
/// Gets or sets the topic alias maximum.
/// This gives the maximum length of the topic alias.
/// </summary>
public ushort? TopicAliasMaximum { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; }
public IMqttPacketInspector PacketInspector { get; set; }
}
}
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Client.Options
{
@@ -256,6 +257,12 @@ namespace MQTTnet.Client.Options
return this;
}
public MqttClientOptionsBuilder WithPacketInspector(IMqttPacketInspector packetInspector)
{
_options.PacketInspector = packetInspector;
return this;
}
public IMqttClientOptions Build()
{
if (_tcpOptions == null && _webSocketOptions == null)
@@ -280,7 +287,9 @@ namespace MQTTnet.Client.Options
#pragma warning disable CS0618 // Type or member is obsolete
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
#pragma warning restore CS0618 // Type or member is obsolete
#if NETCOREAPP3_1
ApplicationProtocols = _tlsParameters.ApplicationProtocols,
#endif
CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors
@@ -19,7 +19,11 @@ namespace MQTTnet.Client.Options
public Func<MqttClientCertificateValidationCallbackContext, bool> CertificateValidationHandler { get; set; }
#if NETCOREAPP3_1 || NET5_0
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls13;
#else
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
#endif
#if WINDOWS_UWP
public IEnumerable<IEnumerable<byte>> Certificates { get; set; }
@@ -27,7 +31,11 @@ namespace MQTTnet.Client.Options
public IEnumerable<X509Certificate> Certificates { get; set; }
#endif
public bool AllowUntrustedCertificates { get; set; }
#if NETCOREAPP3_1
public List<SslApplicationProtocol> ApplicationProtocols { get;set; }
#endif
public bool AllowUntrustedCertificates { get; set; }
public bool IgnoreCertificateChainErrors { get; set; }
@@ -8,7 +8,7 @@ namespace MQTTnet.Client.Options
public int? Port { get; set; }
public int BufferSize { get; set; } = 65536;
public int BufferSize { get; set; } = 8192;
public bool? DualMode { get; set; }
@@ -22,7 +22,15 @@ namespace MQTTnet.Client.Options
public List<X509Certificate> Certificates { get; set; }
#endif
#if NETCOREAPP3_1
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
#endif
#if NETCOREAPP3_1 || NET5_0
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls13;
#else
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
#endif
[Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; }
@@ -8,10 +8,25 @@ namespace MQTTnet.Client.Publishing
{
public ushort? PacketIdentifier { get; set; }
/// <summary>
/// Gets or sets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientPublishReasonCode ReasonCode { get; set; } = MqttClientPublishReasonCode.Success;
/// <summary>
/// Gets or sets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; }
}
}
@@ -5,10 +5,28 @@ namespace MQTTnet.Client.Subscribing
{
public class MqttClientSubscribeOptions
{
/// <summary>
/// Gets or sets a list of topic filters the client wants to subscribe to.
/// Topic filters can include regular topics or wild cards.
/// </summary>
public List<MqttTopicFilter> TopicFilters { get; set; } = new List<MqttTopicFilter>();
/// <summary>
/// Gets or sets the subscription identifier.
/// The client can specify a subscription identifier when subscribing.
/// The broker will establish and store the mapping relationship between this subscription and subscription identifier when successfully create or modify subscription.
/// The broker will return the subscription identifier associated with this PUBLISH packet and the PUBLISH packet to the client when need to forward PUBLISH packets matching this subscription to this client.
/// Hint: MQTT 5 feature only.
/// </summary>
public uint? SubscriptionIdentifier { get; set; }
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; }
}
}
@@ -9,6 +9,13 @@ namespace MQTTnet.Client.Subscribing
{
private readonly MqttClientSubscribeOptions _subscribeOptions = new MqttClientSubscribeOptions();
/// <summary>
/// Adds the user property to the subscribe options.
/// Hint: MQTT 5 feature only.
/// </summary>
/// <param name="name">The property name.</param>
/// <param name="value">The property value.</param>
/// <returns>A new instance of the <see cref="MqttApplicationMessageBuilder"/> class.</returns>
public MqttClientSubscribeOptionsBuilder WithUserProperty(string name, string value)
{
if (name == null) throw new ArgumentNullException(nameof(name));
@@ -27,7 +34,6 @@ namespace MQTTnet.Client.Subscribing
public MqttClientSubscribeOptionsBuilder WithSubscriptionIdentifier(uint? subscriptionIdentifier)
{
_subscribeOptions.SubscriptionIdentifier = subscriptionIdentifier;
return this;
}
@@ -10,8 +10,16 @@ namespace MQTTnet.Client.Subscribing
ResultCode = resultCode;
}
/// <summary>
/// Gets or sets the topic filter.
/// The topic filter can contain topics and wildcards.
/// </summary>
public MqttTopicFilter TopicFilter { get; }
/// <summary>
/// Gets or sets the result code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientSubscribeResultCode ResultCode { get; }
}
}
@@ -5,8 +5,19 @@ namespace MQTTnet.Client.Unsubscribing
{
public class MqttClientUnsubscribeOptions
{
/// <summary>
/// Gets or sets a list of topic filters the client wants to unsubscribe from.
/// Topic filters can include regular topics or wild cards.
/// </summary>
public List<string> TopicFilters { get; set; } = new List<string>();
/// <summary>
/// Gets or sets the user properties.
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
/// The feature is very similar to the HTTP header concept.
/// Hint: MQTT 5 feature only.
/// </summary>
public List<MqttUserProperty> UserProperties { get; set; } = new List<MqttUserProperty>();
}
}
@@ -8,6 +8,13 @@ namespace MQTTnet.Client.Unsubscribing
{
private readonly MqttClientUnsubscribeOptions _unsubscribeOptions = new MqttClientUnsubscribeOptions();
/// <summary>
/// Adds the user property to the unsubscribe options.
/// Hint: MQTT 5 feature only.
/// </summary>
/// <param name="name">The property name.</param>
/// <param name="value">The property value.</param>
/// <returns>A new instance of the <see cref="MqttClientUnsubscribeOptionsBuilder"/> class.</returns>
public MqttClientUnsubscribeOptionsBuilder WithUserProperty(string name, string value)
{
if (name is null) throw new ArgumentNullException(nameof(name));
@@ -16,6 +23,12 @@ namespace MQTTnet.Client.Unsubscribing
return WithUserProperty(new MqttUserProperty(name, value));
}
/// <summary>
/// Adds the user property to the unsubscribe options.
/// Hint: MQTT 5 feature only.
/// </summary>
/// <param name="userProperty">The user property.</param>
/// <returns>A new instance of the <see cref="MqttClientUnsubscribeOptionsBuilder"/> class.</returns>
public MqttClientUnsubscribeOptionsBuilder WithUserProperty(MqttUserProperty userProperty)
{
if (userProperty is null) throw new ArgumentNullException(nameof(userProperty));
@@ -10,8 +10,16 @@ namespace MQTTnet.Client.Unsubscribing
ReasonCode = reasonCode;
}
/// <summary>
/// Gets or sets the topic filter.
/// The topic filter can contain topics and wildcards.
/// </summary>
public string TopicFilter { get; }
/// <summary>
/// Gets or sets the result code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientUnsubscribeResultCode ReasonCode { get; }
}
}
@@ -4,8 +4,6 @@ namespace MQTTnet.Diagnostics
{
public interface IMqttNetLogger
{
event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
IMqttNetScopedLogger CreateScopedLogger(string source);
void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception);
@@ -2,6 +2,7 @@
namespace MQTTnet.Diagnostics
{
[Obsolete("Please pass an instance of the IMqttNetLogger to the factory/server or client. The global logger will be deleted in the future.")]
public static class MqttNetGlobalLogger
{
public static event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
@@ -10,8 +11,6 @@ namespace MQTTnet.Diagnostics
public static void Publish(MqttNetLogMessage logMessage)
{
if (logMessage == null) throw new ArgumentNullException(nameof(logMessage));
LogMessagePublished?.Invoke(null, new MqttNetLogMessagePublishedEventArgs(logMessage));
}
}
@@ -2,7 +2,7 @@
namespace MQTTnet.Diagnostics
{
public class MqttNetLogMessage
public sealed class MqttNetLogMessage
{
public string LogId { get; set; }
@@ -2,18 +2,18 @@
namespace MQTTnet.Diagnostics
{
public class MqttNetLogMessagePublishedEventArgs : EventArgs
public sealed class MqttNetLogMessagePublishedEventArgs : EventArgs
{
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));
TraceMessage = logMessage;
#pragma warning restore CS0618 // Type or member is obsolete
}
[Obsolete("Use new proeprty LogMessage instead.")]
[Obsolete("Use new property LogMessage instead.")]
public MqttNetLogMessage TraceMessage { get; }
public MqttNetLogMessage LogMessage { get; }
+9 -6
View File
@@ -36,13 +36,16 @@ namespace MQTTnet.Diagnostics
return;
}
try
if (parameters?.Length > 0 && message?.Length > 0)
{
message = string.Format(message ?? string.Empty, parameters);
}
catch (FormatException)
{
message = "MESSAGE FORMAT INVALID: " + message;
try
{
message = string.Format(message, parameters);
}
catch (FormatException)
{
message = "MESSAGE FORMAT INVALID: " + message;
}
}
var logMessage = new MqttNetLogMessage
@@ -18,6 +18,11 @@ namespace MQTTnet.Diagnostics
{
logger.Publish(MqttNetLogLevel.Warning, message, parameters, exception);
}
public static void Warning(this IMqttNetScopedLogger logger, string message, params object[] parameters)
{
logger.Publish(MqttNetLogLevel.Warning, message, parameters, null);
}
public static void Error(this IMqttNetScopedLogger logger, Exception exception, string message, params object[] parameters)
{
@@ -0,0 +1,7 @@
namespace MQTTnet.Diagnostics.PacketInspection
{
public interface IMqttPacketInspector
{
void ProcessMqttPacket(ProcessMqttPacketContext context);
}
}
@@ -0,0 +1,9 @@
namespace MQTTnet.Diagnostics.PacketInspection
{
public enum MqttPacketFlowDirection
{
Inbound,
Outbound
}
}
@@ -0,0 +1,9 @@
namespace MQTTnet.Diagnostics.PacketInspection
{
public sealed class ProcessMqttPacketContext
{
public MqttPacketFlowDirection Direction { get; set; }
public byte[] Buffer { get; set; }
}
}
@@ -20,6 +20,10 @@
return "netstandard2.1";
#elif WINDOWS_UWP
return "uap10.0";
#elif NETCOREAPP3_1
return "netcoreapp3.1";
#elif NET5_0
return "net5.0";
#endif
}
}
@@ -13,8 +13,8 @@ namespace MQTTnet.Exceptions
{
}
public MqttCommunicationException(string message)
: base(message)
public MqttCommunicationException(string message, Exception innerException = null)
: base(message, innerException)
{
}
}
@@ -1,10 +1,12 @@
using MQTTnet.Client.Connecting;
using System.Collections.Generic;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Publishing;
using MQTTnet.Client.Subscribing;
using MQTTnet.Client.Unsubscribing;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using MQTTnet.Server;
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
@@ -16,6 +18,8 @@ namespace MQTTnet.Formatter
MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket);
MqttBasePacket CreatePubRecPacket(MqttPublishPacket publishPacket);
MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket);
MqttClientAuthenticateResult CreateClientConnectResult(MqttConnAckPacket connAckPacket);
@@ -30,8 +34,12 @@ namespace MQTTnet.Formatter
MqttSubscribePacket CreateSubscribePacket(MqttClientSubscribeOptions options);
MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult);
MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options);
MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes);
MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options);
MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket);
@@ -1,6 +1,4 @@
using System;
namespace MQTTnet.Formatter
namespace MQTTnet.Formatter
{
public interface IMqttPacketBodyReader
{
@@ -6,13 +6,13 @@ using MQTTnet.Exceptions;
namespace MQTTnet.Formatter
{
public class MqttPacketBodyReader : IMqttPacketBodyReader
public sealed class MqttPacketBodyReader : IMqttPacketBodyReader
{
private readonly byte[] _buffer;
private readonly int _initialOffset;
private readonly int _length;
readonly byte[] _buffer;
readonly int _initialOffset;
readonly int _length;
private int _offset;
int _offset;
public MqttPacketBodyReader(byte[] buffer, int offset, int length)
{
@@ -8,10 +8,10 @@ using MQTTnet.Packets;
namespace MQTTnet.Formatter
{
public class MqttPacketFormatterAdapter
public sealed class MqttPacketFormatterAdapter
{
IMqttPacketFormatter _formatter;
public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion)
: this(protocolVersion, new MqttPacketWriter())
{
@@ -26,7 +26,7 @@ namespace MQTTnet.Formatter
public MqttPacketFormatterAdapter(IMqttPacketWriter writer)
{
Writer = writer;
}
}
public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown;
@@ -39,7 +39,7 @@ namespace MQTTnet.Formatter
return _formatter.DataConverter;
}
}
public IMqttPacketWriter Writer { get; }
public ArraySegment<byte> Encode(MqttBasePacket packet)
@@ -69,32 +69,21 @@ namespace MQTTnet.Formatter
{
var protocolVersion = ParseProtocolVersion(receivedMqttPacket);
// Reset the position of the stream because the protocol version is part of
// Reset the position of the stream because the protocol version is part of
// the regular CONNECT packet. So it will not properly deserialized if this
// data is missing.
receivedMqttPacket.Body.Seek(0);
receivedMqttPacket.BodyReader.Seek(0);
UseProtocolVersion(protocolVersion);
}
private void UseProtocolVersion(MqttProtocolVersion protocolVersion)
{
if (protocolVersion == MqttProtocolVersion.Unknown)
{
throw new InvalidOperationException("MQTT protocol version is invalid.");
}
ProtocolVersion = protocolVersion;
_formatter = GetMqttPacketFormatter(protocolVersion, Writer);
}
public static IMqttPacketFormatter GetMqttPacketFormatter(MqttProtocolVersion protocolVersion, IMqttPacketWriter writer)
{
if (protocolVersion == MqttProtocolVersion.Unknown)
{
throw new InvalidOperationException("MQTT protocol version is invalid.");
}
switch (protocolVersion)
{
case MqttProtocolVersion.V500:
@@ -116,11 +105,22 @@ namespace MQTTnet.Formatter
}
}
MqttProtocolVersion ParseProtocolVersion(ReceivedMqttPacket receivedMqttPacket)
void UseProtocolVersion(MqttProtocolVersion protocolVersion)
{
if (protocolVersion == MqttProtocolVersion.Unknown)
{
throw new InvalidOperationException("MQTT protocol version is invalid.");
}
ProtocolVersion = protocolVersion;
_formatter = GetMqttPacketFormatter(protocolVersion, Writer);
}
static MqttProtocolVersion ParseProtocolVersion(ReceivedMqttPacket receivedMqttPacket)
{
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
if (receivedMqttPacket.Body.Length < 7)
if (receivedMqttPacket.BodyReader.Length < 7)
{
// 2 byte protocol name length
// at least 4 byte protocol name
@@ -128,8 +128,8 @@ namespace MQTTnet.Formatter
throw new MqttProtocolViolationException("CONNECT packet must have at least 7 bytes.");
}
var protocolName = receivedMqttPacket.Body.ReadStringWithLengthPrefix();
var protocolLevel = receivedMqttPacket.Body.ReadByte();
var protocolName = receivedMqttPacket.BodyReader.ReadStringWithLengthPrefix();
var protocolLevel = receivedMqttPacket.BodyReader.ReadByte();
if (protocolName == "MQTT")
{
@@ -1,114 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Channel;
using MQTTnet.Exceptions;
namespace MQTTnet.Formatter
{
public class MqttPacketReader
{
private readonly byte[] _singleByteBuffer = new byte[1];
private readonly IMqttChannel _channel;
public MqttPacketReader(IMqttChannel channel)
{
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
}
public async Task<ReadFixedHeaderResult> ReadFixedHeaderAsync(byte[] fixedHeaderBuffer, CancellationToken cancellationToken)
{
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
var buffer = fixedHeaderBuffer;
var totalBytesRead = 0;
while (totalBytesRead < buffer.Length)
{
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
if (bytesRead == 0)
{
return new ReadFixedHeaderResult
{
ConnectionClosed = true
};
}
totalBytesRead += bytesRead;
}
var hasRemainingLength = buffer[1] != 0;
if (!hasRemainingLength)
{
return new ReadFixedHeaderResult
{
FixedHeader = new MqttFixedHeader(buffer[0], 0, totalBytesRead)
};
}
var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false);
if (!bodyLength.HasValue)
{
return new ReadFixedHeaderResult
{
ConnectionClosed = true
};
}
totalBytesRead += bodyLength.Value;
return new ReadFixedHeaderResult
{
FixedHeader = new MqttFixedHeader(buffer[0], bodyLength.Value, totalBytesRead)
};
}
private async Task<int?> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
{
var offset = 0;
var multiplier = 128;
var value = initialEncodedByte & 127;
int encodedByte = initialEncodedByte;
while ((encodedByte & 128) != 0)
{
offset++;
if (offset > 3)
{
throw new MqttProtocolViolationException("Remaining length is invalid.");
}
if (cancellationToken.IsCancellationRequested)
{
return null;
}
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return null;
}
if (readCount == 0)
{
return null;
}
encodedByte = _singleByteBuffer[0];
value += (encodedByte & 127) * multiplier;
multiplier *= 128;
}
return value;
}
}
}
+54 -36
View File
@@ -13,11 +13,9 @@ namespace MQTTnet.Formatter
/// </summary>
public sealed class MqttPacketWriter : IMqttPacketWriter
{
static readonly ArraySegment<byte> ZeroVariableLengthIntegerArray = new ArraySegment<byte>(new byte[1], 0, 1);
static readonly ArraySegment<byte> ZeroTwoByteIntegerArray = new ArraySegment<byte>(new byte[2], 0, 2);
public static int InitialBufferSize = 4096;
public static int InitialBufferSize = 128;
public static int MaxBufferSize = 4096;
public static int MaxBufferSize = 4096 * 4;
byte[] _buffer = new byte[InitialBufferSize];
@@ -38,59 +36,72 @@ namespace MQTTnet.Formatter
var x = value;
do
{
x = x / 128;
x /= 128;
result++;
} while (x > 0);
return result;
}
public static ArraySegment<byte> EncodeVariableLengthInteger(uint value)
public void WriteVariableLengthInteger(uint value)
{
if (value == 0)
{
return ZeroVariableLengthIntegerArray;
_buffer[_offset] = 0;
IncreasePosition(1);
return;
}
if (value <= 127)
{
return new ArraySegment<byte>(new[] { (byte)value }, 0, 1);
_buffer[_offset] = (byte)value;
IncreasePosition(1);
return;
}
var buffer = new byte[4];
var bufferOffset = 0;
var size = 0;
var x = value;
do
{
var encodedByte = x % 128;
x = x / 128;
x /= 128;
if (x > 0)
{
encodedByte = encodedByte | 128;
encodedByte |= 128;
}
buffer[bufferOffset] = (byte)encodedByte;
bufferOffset++;
_buffer[_offset + size] = (byte)encodedByte;
size++;
} while (x > 0);
return new ArraySegment<byte>(buffer, 0, bufferOffset);
}
public void WriteVariableLengthInteger(uint value)
{
Write(EncodeVariableLengthInteger(value));
IncreasePosition(size);
}
public void WriteWithLengthPrefix(string value)
{
if (string.IsNullOrEmpty(value))
{
Write(ZeroTwoByteIntegerArray);
EnsureAdditionalCapacity(2);
_buffer[_offset] = 0;
_buffer[_offset + 1] = 0;
IncreasePosition(2);
}
else
{
WriteWithLengthPrefix(Encoding.UTF8.GetBytes(value));
var bufferSize = Encoding.UTF8.GetByteCount(value);
EnsureAdditionalCapacity(bufferSize + 2);
_buffer[_offset] = (byte)(bufferSize >> 8);
_buffer[_offset + 1] = (byte)bufferSize;
Encoding.UTF8.GetBytes(value, 0, value.Length, _buffer, _offset + 2);
IncreasePosition(bufferSize + 2);
}
}
@@ -98,13 +109,22 @@ namespace MQTTnet.Formatter
{
if (value == null || value.Length == 0)
{
Write(ZeroTwoByteIntegerArray);
EnsureAdditionalCapacity(2);
_buffer[_offset] = 0;
_buffer[_offset + 1] = 0;
IncreasePosition(2);
}
else
{
EnsureAdditionalCapacity(value.Length + 2);
Write((ushort)value.Length);
Write(value, 0, value.Length);
_buffer[_offset] = (byte)(value.Length >> 8);
_buffer[_offset + 1] = (byte)value.Length;
Array.Copy(value, 0, _buffer, _offset + 2, value.Length);
IncreasePosition(value.Length + 2);
}
}
@@ -143,8 +163,6 @@ namespace MQTTnet.Formatter
public void Write(IMqttPacketWriter propertyWriter)
{
if (propertyWriter == null) throw new ArgumentNullException(nameof(propertyWriter));
if (propertyWriter is MqttPacketWriter writer)
{
if (writer.Length == 0)
@@ -156,7 +174,12 @@ namespace MQTTnet.Formatter
return;
}
throw new InvalidOperationException($"{nameof(propertyWriter)} must be of type {typeof(MqttPacketWriter).Name}");
if (propertyWriter == null)
{
throw new ArgumentNullException(nameof(propertyWriter));
}
throw new InvalidOperationException($"{nameof(propertyWriter)} must be of type {nameof(MqttPacketWriter)}");
}
public void Reset(int length)
@@ -179,8 +202,8 @@ namespace MQTTnet.Formatter
{
// This method frees the used memory by shrinking the buffer. This is required because the buffer
// is used across several messages. In general this is not a big issue because subsequent Ping packages
// have the same size but a very big publish package with 100 MB of payload will increase the buffer
// a lot and the size will never reduced. So this method tries to find a size which can be held in
// have the same size but a very big publish package with 100 MB of payload will increase the buffer
// a lot and the size will never reduced. So this method tries to find a size which can be held in
// memory for a long time without causing troubles.
if (_buffer.Length < MaxBufferSize)
@@ -191,11 +214,6 @@ namespace MQTTnet.Formatter
Array.Resize(ref _buffer, MaxBufferSize);
}
void Write(ArraySegment<byte> buffer)
{
Write(buffer.Array, buffer.Offset, buffer.Count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void EnsureAdditionalCapacity(int additionalCapacity)
{
@@ -9,6 +9,7 @@ using MQTTnet.Packets;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System;
using System.Collections.Generic;
using System.Linq;
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
@@ -39,6 +40,15 @@ namespace MQTTnet.Formatter.V3
};
}
public MqttBasePacket CreatePubRecPacket(MqttPublishPacket publishPacket)
{
return new MqttPubRecPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRecReasonCode.Success
};
}
public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
{
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
@@ -172,6 +182,18 @@ namespace MQTTnet.Formatter.V3
return subscribePacket;
}
public MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult)
{
var subackPacket = new MqttSubAckPacket
{
PacketIdentifier = subscribePacket.PacketIdentifier
};
subackPacket.ReturnCodes.AddRange(subscribeResult.ReturnCodes);
return subackPacket;
}
public MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
@@ -182,6 +204,15 @@ namespace MQTTnet.Formatter.V3
return unsubscribePacket;
}
public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes)
{
return new MqttUnsubAckPacket
{
PacketIdentifier = unsubscribePacket.PacketIdentifier,
ReasonCodes = reasonCodes
};
}
public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
{
if (options.ReasonCode != MqttClientDisconnectReason.NormalDisconnection || options.ReasonString != null)
@@ -63,20 +63,20 @@ namespace MQTTnet.Formatter.V3
switch ((MqttControlPacketType)controlPacketType)
{
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body);
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Disconnect: return DisconnectPacket;
case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket);
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PingReq: return PingReqPacket;
case MqttControlPacketType.PingResp: return PingRespPacket;
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body);
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body);
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.BodyReader);
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
}
@@ -202,18 +202,18 @@ namespace MQTTnet.Formatter.V3
static MqttBasePacket DecodePublishPacket(ReceivedMqttPacket receivedMqttPacket)
{
ThrowIfBodyIsEmpty(receivedMqttPacket.Body);
ThrowIfBodyIsEmpty(receivedMqttPacket.BodyReader);
var retain = (receivedMqttPacket.FixedHeader & 0x1) > 0;
var qualityOfServiceLevel = (MqttQualityOfServiceLevel)(receivedMqttPacket.FixedHeader >> 1 & 0x3);
var dup = (receivedMqttPacket.FixedHeader & 0x8) > 0;
var topic = receivedMqttPacket.Body.ReadStringWithLengthPrefix();
var topic = receivedMqttPacket.BodyReader.ReadStringWithLengthPrefix();
ushort? packetIdentifier = null;
ushort packetIdentifier = 0;
if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{
packetIdentifier = receivedMqttPacket.Body.ReadTwoByteInteger();
packetIdentifier = receivedMqttPacket.BodyReader.ReadTwoByteInteger();
}
var packet = new MqttPublishPacket
@@ -225,9 +225,9 @@ namespace MQTTnet.Formatter.V3
Dup = dup
};
if (!receivedMqttPacket.Body.EndOfStream)
if (!receivedMqttPacket.BodyReader.EndOfStream)
{
packet.Payload = receivedMqttPacket.Body.ReadRemainingData();
packet.Payload = receivedMqttPacket.BodyReader.ReadRemainingData();
}
return packet;
@@ -414,12 +414,12 @@ namespace MQTTnet.Formatter.V3
static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("PubRel packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
}
@@ -432,12 +432,12 @@ namespace MQTTnet.Formatter.V3
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
}
else
{
@@ -471,36 +471,36 @@ namespace MQTTnet.Formatter.V3
static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("PubAck packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
}
static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("PubRec packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
}
static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("PubComp packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
}
@@ -509,12 +509,12 @@ namespace MQTTnet.Formatter.V3
{
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("Subscribe packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
if (packet.TopicFilters?.Count > 0)
{
@@ -530,12 +530,12 @@ namespace MQTTnet.Formatter.V3
static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("SubAck packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
if (packet.ReturnCodes?.Any() == true)
{
@@ -552,12 +552,12 @@ namespace MQTTnet.Formatter.V3
{
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("Unsubscribe packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
if (packet.TopicFilters?.Any() == true)
{
@@ -572,12 +572,12 @@ namespace MQTTnet.Formatter.V3
static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter)
{
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("UnsubAck packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
}
@@ -4,7 +4,7 @@ using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V3
{
public class MqttV311PacketFormatter : MqttV310PacketFormatter
public sealed class MqttV311PacketFormatter : MqttV310PacketFormatter
{
public MqttV311PacketFormatter(IMqttPacketWriter packetWriter)
: base(packetWriter)
@@ -58,6 +58,15 @@ namespace MQTTnet.Formatter.V5
};
}
public MqttBasePacket CreatePubRecPacket(MqttPublishPacket publishPacket)
{
return new MqttPubRecPacket
{
PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRecReasonCode.Success
};
}
public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
{
return new MqttApplicationMessage
@@ -93,6 +102,7 @@ namespace MQTTnet.Formatter.V5
MaximumPacketSize = connAckPacket.Properties?.MaximumPacketSize,
ReasonString = connAckPacket.Properties?.ReasonString,
ReceiveMaximum = connAckPacket.Properties?.ReceiveMaximum,
MaximumQoS = connAckPacket.Properties?.MaximumQoS ?? MqttQualityOfServiceLevel.ExactlyOnce,
ResponseInformation = connAckPacket.Properties?.ResponseInformation,
TopicAliasMaximum = connAckPacket.Properties?.TopicAliasMaximum,
ServerReference = connAckPacket.Properties?.ServerReference,
@@ -143,7 +153,8 @@ namespace MQTTnet.Formatter.V5
AuthenticationMethod = connectionValidatorContext.AuthenticationMethod,
AuthenticationData = connectionValidatorContext.ResponseAuthenticationData,
AssignedClientIdentifier = connectionValidatorContext.AssignedClientIdentifier,
ReasonString = connectionValidatorContext.ReasonString
ReasonString = connectionValidatorContext.ReasonString,
TopicAliasMaximum = ushort.MaxValue
}
};
}
@@ -200,6 +211,18 @@ namespace MQTTnet.Formatter.V5
return packet;
}
public MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult)
{
var subackPacket = new MqttSubAckPacket
{
PacketIdentifier = subscribePacket.PacketIdentifier
};
subackPacket.ReasonCodes.AddRange(subscribeResult.ReasonCodes);
return subackPacket;
}
public MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
@@ -215,6 +238,15 @@ namespace MQTTnet.Formatter.V5
return packet;
}
public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes)
{
return new MqttUnsubAckPacket
{
PacketIdentifier = unsubscribePacket.PacketIdentifier,
ReasonCodes = reasonCodes
};
}
public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
{
var packet = new MqttDisconnectPacket();
@@ -25,21 +25,21 @@ namespace MQTTnet.Formatter.V5
switch ((MqttControlPacketType)controlPacketType)
{
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body);
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Disconnect: return DecodeDisconnectPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket.FixedHeader, receivedMqttPacket.Body);
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body);
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Disconnect: return DecodeDisconnectPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket.FixedHeader, receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PingReq: return DecodePingReqPacket();
case MqttControlPacketType.PingResp: return DecodePingRespPacket();
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body);
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body);
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Auth: return DecodeAuthPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Auth: return DecodeAuthPacket(receivedMqttPacket.BodyReader);
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
}
@@ -254,6 +254,10 @@ namespace MQTTnet.Formatter.V5
{
packet.Properties.ReceiveMaximum = propertiesReader.ReadReceiveMaximum();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MaximumQoS)
{
packet.Properties.MaximumQoS = propertiesReader.ReadMaximumQoS();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AssignedClientIdentifier)
{
packet.Properties.AssignedClientIdentifier = propertiesReader.ReadAssignedClientIdentifier();
@@ -6,22 +6,15 @@ using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V5
{
public class MqttV500PacketEncoder
public sealed class MqttV500PacketEncoder
{
private readonly IMqttPacketWriter _packetWriter;
public MqttV500PacketEncoder()
: this(new MqttPacketWriter())
{
}
readonly IMqttPacketWriter _packetWriter;
public MqttV500PacketEncoder(IMqttPacketWriter packetWriter)
{
_packetWriter = packetWriter;
_packetWriter = packetWriter ?? throw new ArgumentNullException(nameof(packetWriter));
}
public ArraySegment<byte> Encode(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
@@ -52,7 +45,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.FreeBuffer();
}
private static byte EncodePacket(MqttBasePacket packet, IMqttPacketWriter packetWriter)
static byte EncodePacket(MqttBasePacket packet, IMqttPacketWriter packetWriter)
{
switch (packet)
{
@@ -76,7 +69,7 @@ namespace MQTTnet.Formatter.V5
}
}
private static byte EncodeConnectPacket(MqttConnectPacket packet, IMqttPacketWriter packetWriter)
static byte EncodeConnectPacket(MqttConnectPacket packet, IMqttPacketWriter packetWriter)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
@@ -176,7 +169,7 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
}
private static byte EncodeConnAckPacket(MqttConnAckPacket packet, IMqttPacketWriter packetWriter)
static byte EncodeConnAckPacket(MqttConnAckPacket packet, IMqttPacketWriter packetWriter)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
@@ -203,6 +196,7 @@ namespace MQTTnet.Formatter.V5
propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData);
propertiesWriter.WriteRetainAvailable(packet.Properties.RetainAvailable);
propertiesWriter.WriteReceiveMaximum(packet.Properties.ReceiveMaximum);
propertiesWriter.WriteMaximumQoS(packet.Properties.MaximumQoS);
propertiesWriter.WriteAssignedClientIdentifier(packet.Properties.AssignedClientIdentifier);
propertiesWriter.WriteTopicAliasMaximum(packet.Properties.TopicAliasMaximum);
propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
@@ -221,7 +215,7 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
}
private static byte EncodePublishPacket(MqttPublishPacket packet, IMqttPacketWriter packetWriter)
static byte EncodePublishPacket(MqttPublishPacket packet, IMqttPacketWriter packetWriter)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
@@ -235,12 +229,12 @@ namespace MQTTnet.Formatter.V5
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
}
else
{
@@ -253,13 +247,18 @@ namespace MQTTnet.Formatter.V5
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
{
propertiesWriter.WritePayloadFormatIndicator(packet.Properties.PayloadFormatIndicator);
propertiesWriter.WriteMessageExpiryInterval(packet.Properties.MessageExpiryInterval);
propertiesWriter.WriteTopicAlias(packet.Properties.TopicAlias);
propertiesWriter.WriteResponseTopic(packet.Properties.ResponseTopic);
propertiesWriter.WriteCorrelationData(packet.Properties.CorrelationData);
propertiesWriter.WriteSubscriptionIdentifiers(packet.Properties.SubscriptionIdentifiers);
if (packet.Properties.TopicAlias == 0)
{
throw new MqttProtocolViolationException("A Topic Alias of 0 is not permitted. A sender MUST NOT send a PUBLISH packet containing a Topic Alias which has the value 0 [MQTT-3.3.2-8].");
}
propertiesWriter.WriteContentType(packet.Properties.ContentType);
propertiesWriter.WriteCorrelationData(packet.Properties.CorrelationData);
propertiesWriter.WriteMessageExpiryInterval(packet.Properties.MessageExpiryInterval);
propertiesWriter.WritePayloadFormatIndicator(packet.Properties.PayloadFormatIndicator);
propertiesWriter.WriteResponseTopic(packet.Properties.ResponseTopic);
propertiesWriter.WriteSubscriptionIdentifiers(packet.Properties.SubscriptionIdentifiers);
propertiesWriter.WriteTopicAlias(packet.Properties.TopicAlias);
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
@@ -287,12 +286,12 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
}
private static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter)
static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
if (!packet.PacketIdentifier.HasValue)
if (packet.PacketIdentifier == 0)
{
throw new MqttProtocolViolationException("PubAck packet has no packet identifier.");
}
@@ -302,7 +301,7 @@ namespace MQTTnet.Formatter.V5
throw new MqttProtocolViolationException("PubAck packet must contain a reason code.");
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
@@ -320,9 +319,9 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
}
private static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter)
static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter)
{
ThrowIfPacketIdentifierIsInvalid(packet);
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
if (!packet.ReasonCode.HasValue)
{
@@ -336,7 +335,7 @@ namespace MQTTnet.Formatter.V5
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRecReasonCode.Success)
{
@@ -347,9 +346,9 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
}
private static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter)
static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter)
{
ThrowIfPacketIdentifierIsInvalid(packet);
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
if (!packet.ReasonCode.HasValue)
{
@@ -363,7 +362,7 @@ namespace MQTTnet.Formatter.V5
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
if (propertiesWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRelReasonCode.Success)
{
@@ -374,16 +373,16 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
}
private static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter)
static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter)
{
ThrowIfPacketIdentifierIsInvalid(packet);
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
if (!packet.ReasonCode.HasValue)
{
ThrowReasonCodeNotSetException();
}
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
@@ -401,13 +400,13 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
}
private static byte EncodeSubscribePacket(MqttSubscribePacket packet, IMqttPacketWriter packetWriter)
static byte EncodeSubscribePacket(MqttSubscribePacket packet, IMqttPacketWriter packetWriter)
{
if (packet.TopicFilters?.Any() != true) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
ThrowIfPacketIdentifierIsInvalid(packet);
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
@@ -448,13 +447,13 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
}
private static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter)
static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter)
{
if (packet.ReasonCodes?.Any() != true) throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
ThrowIfPacketIdentifierIsInvalid(packet);
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
@@ -473,13 +472,13 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
}
private static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, IMqttPacketWriter packetWriter)
static byte EncodeUnsubscribePacket(MqttUnsubscribePacket packet, IMqttPacketWriter packetWriter)
{
if (packet.TopicFilters?.Any() != true) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
ThrowIfPacketIdentifierIsInvalid(packet);
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
@@ -497,13 +496,13 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
}
private static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter)
static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter)
{
if (packet.ReasonCodes?.Any() != true) throw new MqttProtocolViolationException("At least one reason code must be set[MQTT - 3.8.3 - 3].");
ThrowIfPacketIdentifierIsInvalid(packet);
ThrowIfPacketIdentifierIsInvalid(packet.PacketIdentifier, packet);
packetWriter.Write(packet.PacketIdentifier.Value);
packetWriter.Write(packet.PacketIdentifier);
var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null)
@@ -522,7 +521,7 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
}
private static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, IMqttPacketWriter packetWriter)
static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, IMqttPacketWriter packetWriter)
{
if (!packet.ReasonCode.HasValue)
{
@@ -545,17 +544,17 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Disconnect);
}
private static byte EncodePingReqPacket()
static byte EncodePingReqPacket()
{
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingReq);
}
private static byte EncodePingRespPacket()
static byte EncodePingRespPacket()
{
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingResp);
}
private static byte EncodeAuthPacket(MqttAuthPacket packet, IMqttPacketWriter packetWriter)
static byte EncodeAuthPacket(MqttAuthPacket packet, IMqttPacketWriter packetWriter)
{
packetWriter.Write((byte)packet.ReasonCode);
@@ -573,14 +572,16 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Auth);
}
private static void ThrowReasonCodeNotSetException()
static void ThrowReasonCodeNotSetException()
{
throw new MqttProtocolViolationException("The ReasonCode must be set for MQTT version 5.");
}
private static void ThrowIfPacketIdentifierIsInvalid(IMqttPacketWithIdentifier packet)
static void ThrowIfPacketIdentifierIsInvalid(ushort packetIdentifier, MqttBasePacket packet)
{
if (!packet.PacketIdentifier.HasValue)
// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH(in cases where QoS > 0) Control Packets MUST contain a non-zero 16 - bit Packet Identifier[MQTT - 2.3.1 - 1].
if (packetIdentifier == 0)
{
throw new MqttProtocolViolationException($"Packet identifier is not set for {packet.GetType().Name}.");
}
@@ -4,15 +4,10 @@ using MQTTnet.Packets;
namespace MQTTnet.Formatter.V5
{
public class MqttV500PacketFormatter : IMqttPacketFormatter
public sealed class MqttV500PacketFormatter : IMqttPacketFormatter
{
private readonly MqttV500PacketEncoder _encoder;
private readonly MqttV500PacketDecoder _decoder = new MqttV500PacketDecoder();
public MqttV500PacketFormatter()
{
_encoder = new MqttV500PacketEncoder();
}
readonly MqttV500PacketDecoder _decoder = new MqttV500PacketDecoder();
readonly MqttV500PacketEncoder _encoder;
public MqttV500PacketFormatter(IMqttPacketWriter writer)
{
@@ -82,6 +82,17 @@ namespace MQTTnet.Formatter.V5
return _body.ReadTwoByteInteger();
}
public MqttQualityOfServiceLevel ReadMaximumQoS()
{
byte value = _body.ReadByte();
if (value > 1)
{
throw new MqttProtocolViolationException($"Unexpected Maximum QoS value: {value}");
}
return (MqttQualityOfServiceLevel)value;
}
public string ReadAssignedClientIdentifier()
{
return _body.ReadStringWithLengthPrefix();
@@ -5,10 +5,9 @@ using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V5
{
public class MqttV500PropertiesWriter
public sealed class MqttV500PropertiesWriter
{
// TODO: Consider lazy init on first write to avoid useless allocations.
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
public int Length => _packetWriter.Length;
@@ -133,6 +132,17 @@ namespace MQTTnet.Formatter.V5
Write(MqttPropertyId.ReceiveMaximum, value);
}
public void WriteMaximumQoS(MqttQualityOfServiceLevel? value)
{
if (!value.HasValue || value.Value > MqttQualityOfServiceLevel.AtLeastOnce)
{
return;
}
_packetWriter.Write((byte)MqttPropertyId.MaximumQoS);
_packetWriter.Write((byte)value.Value);
}
public void WriteMaximumPacketSize(uint? value)
{
WriteAsFourByteInteger(MqttPropertyId.MaximumPacketSize, value);
@@ -178,7 +188,7 @@ namespace MQTTnet.Formatter.V5
Write(MqttPropertyId.ResponseInformation, value);
}
private void Write(MqttPropertyId id, bool? value)
void Write(MqttPropertyId id, bool? value)
{
if (!value.HasValue)
{
@@ -189,7 +199,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.Write(value.Value ? (byte)0x1 : (byte)0x0);
}
private void Write(MqttPropertyId id, byte? value)
void Write(MqttPropertyId id, byte? value)
{
if (!value.HasValue)
{
@@ -200,7 +210,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.Write(value.Value);
}
private void Write(MqttPropertyId id, ushort? value)
void Write(MqttPropertyId id, ushort? value)
{
if (!value.HasValue)
{
@@ -211,7 +221,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.Write(value.Value);
}
private void WriteAsVariableLengthInteger(MqttPropertyId id, uint? value)
void WriteAsVariableLengthInteger(MqttPropertyId id, uint? value)
{
if (!value.HasValue)
{
@@ -222,7 +232,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.WriteVariableLengthInteger(value.Value);
}
private void WriteAsFourByteInteger(MqttPropertyId id, uint? value)
void WriteAsFourByteInteger(MqttPropertyId id, uint? value)
{
if (!value.HasValue)
{
@@ -236,7 +246,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.Write((byte)value.Value);
}
private void Write(MqttPropertyId id, string value)
void Write(MqttPropertyId id, string value)
{
if (value == null)
{
@@ -247,7 +257,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.WriteWithLengthPrefix(value);
}
private void Write(MqttPropertyId id, byte[] value)
void Write(MqttPropertyId id, byte[] value)
{
if (value == null)
{
@@ -4,6 +4,10 @@ namespace MQTTnet
{
public interface IApplicationMessageReceiver
{
/// <summary>
/// Gets or sets the application message received handler that is fired every time a new message is received on the client's subscriptions.
/// Hint: Initialize handlers before you connect the client to avoid issues.
/// </summary>
IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; }
}
}
@@ -4,6 +4,7 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Exceptions;
namespace MQTTnet.Implementations
{
@@ -33,6 +34,9 @@ namespace MQTTnet.Implementations
public bool NoDelay
{
// We cannot use the _NoDelay_ property from the socket because there is an issue in .NET 4.5.2, 4.6.
// The decompiled code is: this.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.Debug, value ? 1 : 0);
// Which is wrong because the "NoDelay" should be set and not "Debug".
get => (int)_socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay) > 0;
set => _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
}
@@ -48,13 +52,19 @@ namespace MQTTnet.Implementations
get => _socket.ReceiveBufferSize;
set => _socket.ReceiveBufferSize = value;
}
public int SendBufferSize
{
get => _socket.SendBufferSize;
set => _socket.SendBufferSize = value;
}
public int SendTimeout
{
get => _socket.SendTimeout;
set => _socket.SendTimeout = value;
}
public EndPoint RemoteEndPoint => _socket.RemoteEndPoint;
public bool ReuseAddress
@@ -69,11 +79,10 @@ namespace MQTTnet.Implementations
{
#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
return new CrossPlatformSocket(clientSocket);
}
catch (ObjectDisposedException)
{
@@ -115,9 +124,19 @@ namespace MQTTnet.Implementations
_networkStream = new NetworkStream(_socket, true);
}
}
catch (SocketException socketException)
{
if (socketException.SocketErrorCode == SocketError.OperationAborted)
{
throw new OperationCanceledException();
}
throw new MqttCommunicationException($"Error while connecting with host '{host}:{port}'.", socketException);
}
catch (ObjectDisposedException)
{
// This will happen when _socket.EndConnect gets called by Task library but the socket is already disposed.
cancellationToken.ThrowIfCancellationRequested();
}
}
@@ -3,6 +3,7 @@ using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using System;
using MQTTnet.Channel;
namespace MQTTnet.Implementations
{
@@ -19,16 +20,19 @@ namespace MQTTnet.Implementations
{
if (options == null) throw new ArgumentNullException(nameof(options));
IMqttChannel channel;
switch (options.ChannelOptions)
{
case MqttClientTcpOptions _:
{
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter()), _logger);
channel = new MqttTcpChannel(options);
break;
}
case MqttClientWebSocketOptions webSocketOptions:
{
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter()), _logger);
channel = new MqttWebSocketChannel(webSocketOptions);
break;
}
default:
@@ -36,6 +40,9 @@ namespace MQTTnet.Implementations
throw new NotSupportedException();
}
}
var packetFormatterAdapter = new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter());
return new MqttChannelAdapter(channel, packetFormatterAdapter, options.PacketInspector, _logger);
}
}
}
@@ -75,11 +75,23 @@ namespace MQTTnet.Implementations
_socket.Control.IgnorableServerCertificateErrors.Add(ignorableChainValidationResult);
}
#if NETCOREAPP3_1 || NET5_0
var socketProtectionLevel = SocketProtectionLevel.Tls13;
if (_options.TlsOptions.SslProtocol == SslProtocols.Tls12)
{
socketProtectionLevel = SocketProtectionLevel.Tls12;
}
else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
{
socketProtectionLevel = SocketProtectionLevel.Tls11;
}
#else
var socketProtectionLevel = SocketProtectionLevel.Tls12;
if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
{
socketProtectionLevel = SocketProtectionLevel.Tls11;
}
#endif
else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls)
{
socketProtectionLevel = SocketProtectionLevel.Tls10;
@@ -129,7 +141,7 @@ namespace MQTTnet.Implementations
if (options.TlsOptions.Certificates.Count > 1)
{
throw new NotSupportedException("Only one client certificate is supported for UWP.");
throw new NotSupportedException("Only one client certificate is supported when using 'uap10.0'.");
}
return new Certificate(options.TlsOptions.Certificates.First().AsBuffer());
@@ -10,20 +10,21 @@ using System.Runtime.ExceptionServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Exceptions;
namespace MQTTnet.Implementations
{
public sealed class MqttTcpChannel : IMqttChannel
{
readonly IMqttClientOptions _clientOptions;
readonly MqttClientTcpOptions _options;
readonly MqttClientTcpOptions _tcpOptions;
Stream _stream;
public MqttTcpChannel(IMqttClientOptions clientOptions)
{
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
_options = (MqttClientTcpOptions)clientOptions.ChannelOptions;
_tcpOptions = (MqttClientTcpOptions)clientOptions.ChannelOptions;
IsSecureConnection = clientOptions.ChannelOptions?.TlsOptions?.UseTls == true;
}
@@ -49,43 +50,57 @@ namespace MQTTnet.Implementations
CrossPlatformSocket socket = null;
try
{
if (_options.AddressFamily == AddressFamily.Unspecified)
if (_tcpOptions.AddressFamily == AddressFamily.Unspecified)
{
socket = new CrossPlatformSocket();
}
else
{
socket = new CrossPlatformSocket(_options.AddressFamily);
socket = new CrossPlatformSocket(_tcpOptions.AddressFamily);
}
socket.ReceiveBufferSize = _options.BufferSize;
socket.SendBufferSize = _options.BufferSize;
socket.NoDelay = _options.NoDelay;
socket.ReceiveBufferSize = _tcpOptions.BufferSize;
socket.SendBufferSize = _tcpOptions.BufferSize;
socket.SendTimeout = (int)_clientOptions.CommunicationTimeout.TotalMilliseconds;
socket.NoDelay = _tcpOptions.NoDelay;
if (_options.DualMode.HasValue)
if (_tcpOptions.DualMode.HasValue)
{
// It is important to avoid setting the flag if no specific value is set by the user
// because on IPv4 only networks the setter will always throw an exception. Regardless
// of the actual value.
socket.DualMode = _options.DualMode.Value;
socket.DualMode = _tcpOptions.DualMode.Value;
}
await socket.ConnectAsync(_options.Server, _options.GetPort(), cancellationToken).ConfigureAwait(false);
await socket.ConnectAsync(_tcpOptions.Server, _tcpOptions.GetPort(), cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
var networkStream = socket.GetStream();
if (_options.TlsOptions?.UseTls == true)
if (_tcpOptions.TlsOptions?.UseTls == true)
{
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
try
{
await sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), _options.TlsOptions.SslProtocol, !_options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
#if NETCOREAPP3_1
var sslOptions = new SslClientAuthenticationOptions
{
ApplicationProtocols = _tcpOptions.TlsOptions.ApplicationProtocols,
ClientCertificates = LoadCertificates(),
EnabledSslProtocols = _tcpOptions.TlsOptions.SslProtocol,
CertificateRevocationCheckMode = _tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : X509RevocationMode.Online,
TargetHost = _tcpOptions.Server
};
await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
#else
await sslStream.AuthenticateAsClientAsync(_tcpOptions.Server, LoadCertificates(), _tcpOptions.TlsOptions.SslProtocol, !_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
#endif
}
catch
{
#if NETSTANDARD2_1
#if NETSTANDARD2_1 || NETCOREAPP3_1
await sslStream.DisposeAsync().ConfigureAwait(false);
#else
sslStream.Dispose();
@@ -118,22 +133,25 @@ 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));
cancellationToken.ThrowIfCancellationRequested();
try
{
var stream = _stream;
if (stream == null)
{
return 0;
}
if (!stream.CanRead)
{
return 0;
}
// Workaround for: https://github.com/dotnet/corefx/issues/24430
using (cancellationToken.Register(Dispose))
{
var stream = _stream;
if (stream == null)
{
throw new ObjectDisposedException(nameof(stream));
}
return await stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
}
}
@@ -155,8 +173,6 @@ namespace MQTTnet.Implementations
public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer is null) throw new ArgumentNullException(nameof(buffer));
cancellationToken.ThrowIfCancellationRequested();
try
@@ -168,7 +184,7 @@ namespace MQTTnet.Implementations
if (stream == null)
{
throw new ObjectDisposedException(nameof(stream));
throw new MqttCommunicationException("The TCP connection is closed.");
}
await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
@@ -176,6 +192,7 @@ namespace MQTTnet.Implementations
}
catch (ObjectDisposedException)
{
throw new MqttCommunicationException("The TCP connection is closed.");
}
catch (IOException exception)
{
@@ -209,18 +226,18 @@ namespace MQTTnet.Implementations
bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
#region OBSOLETE
#region OBSOLETE
#pragma warning disable CS0618 // Type or member is obsolete
var certificateValidationCallback = _options?.TlsOptions?.CertificateValidationCallback;
var certificateValidationCallback = _tcpOptions?.TlsOptions?.CertificateValidationCallback;
#pragma warning restore CS0618 // Type or member is obsolete
if (certificateValidationCallback != null)
{
return certificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions);
}
#endregion
#endregion
var certificateValidationHandler = _options?.TlsOptions?.CertificateValidationHandler;
var certificateValidationHandler = _tcpOptions?.TlsOptions?.CertificateValidationHandler;
if (certificateValidationHandler != null)
{
var context = new MqttClientCertificateValidationCallbackContext
@@ -228,7 +245,7 @@ namespace MQTTnet.Implementations
Certificate = x509Certificate,
Chain = chain,
SslPolicyErrors = sslPolicyErrors,
ClientOptions = _options
ClientOptions = _tcpOptions
};
return certificateValidationHandler(context);
@@ -241,7 +258,7 @@ namespace MQTTnet.Implementations
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.OfflineRevocation))
{
if (_options?.TlsOptions?.IgnoreCertificateRevocationErrors != true)
if (_tcpOptions?.TlsOptions?.IgnoreCertificateRevocationErrors != true)
{
return false;
}
@@ -249,24 +266,24 @@ namespace MQTTnet.Implementations
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain))
{
if (_options?.TlsOptions?.IgnoreCertificateChainErrors != true)
if (_tcpOptions?.TlsOptions?.IgnoreCertificateChainErrors != true)
{
return false;
}
}
return _options?.TlsOptions?.AllowUntrustedCertificates == true;
return _tcpOptions?.TlsOptions?.AllowUntrustedCertificates == true;
}
X509CertificateCollection LoadCertificates()
{
var certificates = new X509CertificateCollection();
if (_options.TlsOptions.Certificates == null)
if (_tcpOptions.TlsOptions.Certificates == null)
{
return certificates;
}
foreach (var certificate in _options.TlsOptions.Certificates)
foreach (var certificate in _tcpOptions.TlsOptions.Certificates)
{
certificates.Add(certificate);
}
@@ -48,7 +48,7 @@ namespace MQTTnet.Implementations
if (options.TlsEndpointOptions.IsEnabled)
{
throw new NotSupportedException("TLS servers are not supported for UWP apps.");
throw new NotSupportedException("TLS servers are not supported when using 'uap10.0'.");
}
}
@@ -89,7 +89,7 @@ namespace MQTTnet.Implementations
}
}
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, clientCertificate, _options), new MqttPacketFormatterAdapter(new MqttPacketWriter()), _rootLogger))
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, clientCertificate, _options), new MqttPacketFormatterAdapter(new MqttPacketWriter()), null, _rootLogger))
{
await clientHandler(clientAdapter).ConfigureAwait(false);
}
@@ -113,7 +113,7 @@ namespace MQTTnet.Implementations
}
catch (Exception exception)
{
_logger.Error(exception, "Error while cleaning up client connection");
_logger.Error(exception, "Error while cleaning up client connection.");
}
}
}
@@ -47,7 +47,7 @@ namespace MQTTnet.Implementations
{
throw new ArgumentException("TLS certificate is not set.");
}
var tlsCertificate = options.TlsEndpointOptions.CertificateProvider.GetCertificate();
if (!tlsCertificate.HasPrivateKey)
{
@@ -73,16 +73,22 @@ namespace MQTTnet.Implementations
void Cleanup()
{
_cancellationTokenSource?.Cancel(false);
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
foreach (var listener in _listeners)
try
{
listener.Dispose();
_cancellationTokenSource?.Cancel(false);
}
finally
{
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
_listeners.Clear();
foreach (var listener in _listeners)
{
listener.Dispose();
}
_listeners.Clear();
}
}
void RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate, CancellationToken cancellationToken)
@@ -59,7 +59,7 @@ namespace MQTTnet.Implementations
_localEndPoint = new IPEndPoint(boundIp, _options.Port);
_logger.Info($"Starting TCP listener for {_localEndPoint} TLS={_tlsCertificate != null}.");
_logger.Info("Starting TCP listener for {0} TLS={1}.", _localEndPoint, _tlsCertificate != null);
_socket = new CrossPlatformSocket(_addressFamily);
@@ -77,8 +77,8 @@ namespace MQTTnet.Implementations
_socket.Bind(_localEndPoint);
_socket.Listen(_options.ConnectionBacklog);
Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).Forget(_logger);
Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).RunInBackground(_logger);
return true;
}
@@ -115,7 +115,7 @@ namespace MQTTnet.Implementations
continue;
}
Task.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken).Forget(_logger);
Task.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken).RunInBackground(_logger);
}
catch (OperationCanceledException)
{
@@ -131,7 +131,7 @@ namespace MQTTnet.Implementations
}
}
_logger.Error(exception, $"Error while accepting connection at TCP listener {_localEndPoint} TLS={_tlsCertificate != null}.");
_logger.Error(exception, "Error while accepting connection at TCP listener {0} TLS={1}.", _localEndPoint, _tlsCertificate != null);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
}
}
@@ -152,9 +152,7 @@ namespace MQTTnet.Implementations
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
clientSocket.NoDelay = _options.NoDelay;
stream = clientSocket.GetStream();
X509Certificate2 clientCertificate = null;
if (_tlsCertificate != null)
@@ -180,7 +178,11 @@ namespace MQTTnet.Implementations
var clientHandler = ClientHandler;
if (clientHandler != null)
{
using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(stream, remoteEndPoint, clientCertificate), new MqttPacketFormatterAdapter(new MqttPacketWriter()), _rootLogger))
using (var clientAdapter = new MqttChannelAdapter(
new MqttTcpChannel(stream, remoteEndPoint, clientCertificate),
new MqttPacketFormatterAdapter(new MqttPacketWriter()),
null,
_rootLogger))
{
await clientHandler(clientAdapter).ConfigureAwait(false);
}
@@ -208,17 +210,17 @@ namespace MQTTnet.Implementations
{
stream?.Dispose();
clientSocket?.Dispose();
_logger.Verbose("Client '{0}' disconnected at TCP listener '{1}, {2}'.",
remoteEndPoint,
_localEndPoint,
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
}
catch (Exception disposeException)
{
_logger.Error(disposeException, "Error while cleaning up client connection");
}
}
_logger.Verbose("Client '{0}' disconnected at TCP listener '{1}, {2}'.",
remoteEndPoint,
_localEndPoint,
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
}
}
}
@@ -156,10 +156,20 @@ namespace MQTTnet.Implementations
}
var certificateValidationHandler = _options.TlsOptions?.CertificateValidationHandler;
#if NETSTANDARD2_1
if (certificateValidationHandler != null)
{
clientWebSocket.Options.RemoteCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) =>
#if NETSTANDARD1_3
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'netstandard1.3'.");
#elif NETSTANDARD2_0
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'netstandard2.0'.");
#elif WINDOWS_UWP
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'uap10.0'.");
#elif NET452
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net452'.");
#elif NET461
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net461'.");
#else
clientWebSocket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
// TODO: Find a way to add client options to same callback. Problem is that they have a different type.
var context = new MqttClientCertificateValidationCallbackContext
@@ -171,14 +181,9 @@ namespace MQTTnet.Implementations
};
return certificateValidationHandler(context);
});
}
#else
if (certificateValidationHandler != null)
{
throw new NotSupportedException("The remote certificate validation callback for Web Sockets is only supported for netstandard 2.1+");
}
};
#endif
}
}
void Cleanup()
@@ -207,9 +212,9 @@ namespace MQTTnet.Implementations
}
#if WINDOWS_UWP
throw new NotSupportedException("Proxies are not supported in UWP.");
throw new NotSupportedException("Proxies are not supported when using 'uap10.0'.");
#elif NETSTANDARD1_3
throw new NotSupportedException("Proxies are not supported in netstandard 1.3.");
throw new NotSupportedException("Proxies are not supported when using 'netstandard 1.3'.");
#else
var proxyUri = new Uri(_options.ProxyOptions.Address);

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