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 # 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="MQTTnet" version="$nugetVersion" />
<dependency id="Microsoft.AspNetCore.Http.Connections" version="1.0.0" /> <dependency id="Microsoft.AspNetCore.Http.Connections" version="1.0.0" />
</group> </group>
<group targetFramework="netcoreapp3.1"> <group targetFramework="netcoreapp3.1">
<dependency id="MQTTnet" version="$nugetVersion" /> <dependency id="MQTTnet" version="$nugetVersion" />
</group> </group>
<group targetFramework="net5.0">
<dependency id="MQTTnet" version="$nugetVersion" />
</group>
</dependencies> </dependencies>
</metadata> </metadata>
@@ -33,10 +37,13 @@
<!-- Images --> <!-- Images -->
<file src="..\Images\Logo_128x128.png" target="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 --> <!-- .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 --> <!-- .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> </files>
</package> </package>
@@ -13,7 +13,7 @@
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description> <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> <releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright> <copyright>Copyright Christian Kratky 2016-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> <dependencies>
<dependency id="MQTTnet" version="$nugetVersion" /> <dependency id="MQTTnet" version="$nugetVersion" />
</dependencies> </dependencies>
@@ -26,20 +26,23 @@
<!-- Images --> <!-- Images -->
<file src="..\Images\Logo_128x128.png" target="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 --> <!-- .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 --> <!-- .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 --> <!-- 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 --> <!-- .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. --> <!-- .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. --> <!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files> </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> <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> <releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright> <copyright>Copyright Christian Kratky 2016-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> <dependencies>
<dependency id="MQTTnet" version="$nugetVersion" /> <dependency id="MQTTnet" version="$nugetVersion" />
</dependencies> </dependencies>
@@ -26,20 +26,23 @@
<!-- Images --> <!-- Images -->
<file src="..\Images\Logo_128x128.png" target="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 --> <!-- .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 --> <!-- .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 --> <!-- 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 --> <!-- .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. --> <!-- .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. --> <!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files> </files>
@@ -13,7 +13,7 @@
<description>This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.</description> <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> <releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright> <copyright>Copyright Christian Kratky 2016-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> <dependencies>
<group targetFramework="netstandard1.3"> <group targetFramework="netstandard1.3">
<dependency id="MQTTnet" version="$nugetVersion" /> <dependency id="MQTTnet" version="$nugetVersion" />
@@ -41,20 +41,23 @@
<!-- Images --> <!-- Images -->
<file src="..\Images\Logo_128x128.png" target="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 --> <!-- .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 --> <!-- .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 --> <!-- 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 --> <!-- .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. --> <!-- .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. --> <!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files> </files>
+41 -19
View File
@@ -12,25 +12,35 @@
<requireLicenseAcceptance>true</requireLicenseAcceptance> <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> <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> <releaseNotes>
* [LowLevelClient] Fixed a null reference exception when connecting to a not existing server (thanks to @SGStino). * [Core] Added .NET 5.0 TFMs (thanks to @StefanOssendorf, @JanEggers).
* [RcpClient] Adjusted some namespaces (BREAKING CHANGE!). * [Core] Added support for TLS 1.3 (requires .NET Core 3.1+) (thanks to @Dvergatal).
* [AspNetCore] Adjusted some namespaces (BREAKING CHANGE!). * [Core] Added support for _MaximumQoS_ transmit when using MQTTv5 (thanks to @nayato).
* [Server] Adjusted some namespaces (BREAKING CHANGE!). * [Core] Aligned the format of some log messages.
* [Server] Added state checks (throw if not started etc.) for most server APIs. * [Client] Fixed an issue with connection state detection (thanks to @tobiasfrick).
* [Server] Exposed real X509Certificate2 (instead byte array) to TLS options (thanks to @borigas). * [Client] Fixed an issue when receiving partial QoS 2 packets after a reconnect.
* [Server] Fixed memory leak with TCP sockets (MqttServer is now Disposable!) (BREAKING CHANGE!). * [Client] Added new API for raw packet inspection.
* [Core] Fixed a null reference exception in the MqttTcpChannel with WriteAsync and ReadAsync. * [ManagedClient] Removed _AutoReconnect_ since this does no longer work (BREAKING CHANGE!).
* [Core] Added server interceptor for undelivered messages (thanks to @cshark-inator). * [RpcClient] Moved the RPC topic validation to the topic generation strategy.
* [nuget] Added support for SourceLink (thanks to @JTOne123). * [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> </releaseNotes>
<copyright>Copyright Christian Kratky 2016-2020</copyright> <copyright>Copyright Christian Kratky 2016-2020</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin 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> <dependencies>
<group targetFramework="netstandard1.3"> <group targetFramework="netstandard1.3">
<dependency id="NETStandard.Library" version="1.3.0" /> <dependency id="NETStandard.Library" version="1.3.0" />
<dependency id="System.Net.Security" version="4.3.2" /> <dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" /> <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>
<group targetFramework="netstandard2.0"> <group targetFramework="netstandard2.0">
<dependency id="NETStandard.Library" version="2.0.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" 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>
<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"> <group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" /> <dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.10" />
</group> </group>
@@ -57,23 +73,29 @@
<!-- Images --> <!-- Images -->
<file src="..\Images\Logo_128x128.png" target="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 --> <!-- .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 --> <!-- .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 --> <!-- .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 --> <!-- 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 --> <!-- .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. --> <!-- .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. --> <!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
</files> </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="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.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="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netcoreapp3.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx" &$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="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 # 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 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 Copy-Item MQTTnet.AspNetCore.nuspec -Destination MQTTnet.AspNetCore.nuspec.old -Force
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec (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 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.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 .\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.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.Rpc.nuspec.old -Destination MQTTnet.Extensions.Rpc.nuspec -Force
Move-Item MQTTnet.Extensions.ManagedClient.nuspec.old -Destination MQTTnet.Extensions.ManagedClient.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 # 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" $source = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\Release\netcoreapp3.1\publish"
$destination = (Convert-Path .) + "\..\Source\MQTTnet.Server\bin\MQTTnet.Server-Portable-v$nugetVersion.zip" $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 # 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" $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" $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 # 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" $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" $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 # 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" $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" $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 EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}" 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 EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Blazor", "Blazor", "{61B165A0-5AA8-4E04-A53D-A22A84AA6EB7}"
EndProject 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 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 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 EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution 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|x64.Build.0 = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = 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 {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.ActiveCfg = Debug|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU {3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
@@ -328,7 +303,6 @@ Global
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4} {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} {3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4} {3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {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_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_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_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 ### General
* Async support * 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) * Extensible communication channels (e.g. In-Memory, TCP, TCP+TLS, WS)
* Lightweight (only the low level implementation of MQTT, no overhead) * Lightweight (only the low level implementation of MQTT, no overhead)
* Performance optimized (processing ~70.000 messages / second)* * 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 ## Supported frameworks
* .NET 5.0+
* .NET Standard 1.3+ * .NET Standard 1.3+
* .NET Core 1.1+ * .NET Core 1.1+
* .NET Core App 1.1+ * .NET Core App 1.1+
* .NET Framework 4.5.2+ (x86, x64, AnyCPU) * .NET Framework 4.5.2+
* Mono 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.Android 7.5+
* Xamarin.iOS 10.14+ * 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 ## Supported MQTT versions
@@ -97,11 +114,14 @@ Also additions and updates in the Wiki are welcome.
This library is used in the following projects: 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>) * 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 (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)) * 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>) * 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. 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 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal 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); _sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool); _receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
} }
#if NETCOREAPP3_1 #if NETCOREAPP3_1 || NET5_0
public override ValueTask DisposeAsync() public override ValueTask DisposeAsync()
#else #else
public Task DisposeAsync() public Task DisposeAsync()
@@ -53,7 +53,7 @@ namespace MQTTnet.AspNetCore.Client.Tcp
_socket?.Dispose(); _socket?.Dispose();
#if NETCOREAPP3_1 #if NETCOREAPP3_1 || NET5_0
return base.DisposeAsync(); return base.DisposeAsync();
} }
@@ -12,6 +12,7 @@ namespace MQTTnet.AspNetCore.Extensions
#if NETCOREAPP3_1 #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(...).")] [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 #endif
#if NETCOREAPP3_1 || NETCOREAPP2_1 || NETSTANDARD
public static void MapMqtt(this ConnectionsRouteBuilder connection, PathString path) public static void MapMqtt(this ConnectionsRouteBuilder connection, PathString path)
{ {
connection.MapConnectionHandler<MqttConnectionHandler>(path, options => connection.MapConnectionHandler<MqttConnectionHandler>(path, options =>
@@ -19,5 +20,6 @@ namespace MQTTnet.AspNetCore.Extensions
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol; 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.Builder;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1</TargetFrameworks> <TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks>
<Product /> <Product />
<Company /> <Company />
<Authors /> <Authors />
@@ -21,11 +21,11 @@
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants> <DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'"> <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' ">
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup> </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" /> <PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.1.0" />
</ItemGroup> </ItemGroup>
@@ -66,10 +66,10 @@ namespace MQTTnet.AspNetCore
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; } public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public long BytesSent { get; set; } public long BytesSent { get; set; }
public long BytesReceived { get; set; } public long BytesReceived { get; set; }
public Action ReadingPacketStartedCallback { get; set; } public bool IsReadingPacket { get; private set; }
public Action ReadingPacketCompletedCallback { get; set; }
IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>(); IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
@@ -92,7 +92,7 @@ namespace MQTTnet.AspNetCore
return Task.CompletedTask; return Task.CompletedTask;
} }
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken) public async Task<MqttBasePacket> ReceivePacketAsync(CancellationToken cancellationToken)
{ {
var input = Connection.Transport.Input; var input = Connection.Transport.Input;
@@ -128,7 +128,7 @@ namespace MQTTnet.AspNetCore
else else
{ {
// we did receive something but the message is not yet complete // we did receive something but the message is not yet complete
ReadingPacketStartedCallback?.Invoke(); IsReadingPacket = true;
} }
} }
else if (readResult.IsCompleted) else if (readResult.IsCompleted)
@@ -147,14 +147,14 @@ namespace MQTTnet.AspNetCore
} }
catch (Exception e) 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); _input?.Complete(e);
_output?.Complete(e); _output?.Complete(e);
throw; throw;
} }
finally finally
{ {
ReadingPacketCompletedCallback?.Invoke(); IsReadingPacket = false;
} }
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@@ -167,7 +167,7 @@ namespace MQTTnet.AspNetCore
BytesSent = 0; BytesSent = 0;
} }
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken) public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{ {
var formatter = PacketFormatterAdapter; var formatter = PacketFormatterAdapter;
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false)) using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
@@ -180,6 +180,7 @@ namespace MQTTnet.AspNetCore
{ {
BytesSent += msg.Length; BytesSent += msg.Length;
} }
PacketFormatterAdapter.FreeBuffer(); PacketFormatterAdapter.FreeBuffer();
} }
} }
@@ -49,7 +49,7 @@ namespace MQTTnet.AspNetCore
var formatter = new MqttPacketFormatterAdapter(writer); var formatter = new MqttPacketFormatterAdapter(writer);
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate); 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); await clientHandler(channelAdapter).ConfigureAwait(false);
} }
@@ -4,30 +4,46 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Client;
namespace MQTTnet.Extensions.ManagedClient namespace MQTTnet.Extensions.ManagedClient
{ {
public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
{ {
bool IsStarted { get; } /// <summary>
bool IsConnected { get; } /// Gets the internally used MQTT client.
int PendingApplicationMessagesCount { get; } /// This property should be used with caution because manipulating the internal client might break the managed client.
IManagedMqttClientOptions Options { get; } /// </summary>
IMqttClient InternalClient { get; }
bool IsStarted { get; }
bool IsConnected { get; }
int PendingApplicationMessagesCount { get; }
IManagedMqttClientOptions Options { get; }
IMqttClientConnectedHandler ConnectedHandler { get; set; } IMqttClientConnectedHandler ConnectedHandler { get; set; }
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; } IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; } IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; } IApplicationMessageSkippedHandler ApplicationMessageSkippedHandler { get; set; }
IConnectingFailedHandler ConnectingFailedHandler { get; set; } IConnectingFailedHandler ConnectingFailedHandler { get; set; }
ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; } ISynchronizingSubscriptionsFailedHandler SynchronizingSubscriptionsFailedHandler { get; set; }
Task StartAsync(IManagedMqttClientOptions options); Task StartAsync(IManagedMqttClientOptions options);
Task StopAsync(); Task StopAsync();
Task PingAsync(CancellationToken cancellationToken); Task PingAsync(CancellationToken cancellationToken);
Task SubscribeAsync(IEnumerable<MqttTopicFilter> topicFilters); Task SubscribeAsync(IEnumerable<MqttTopicFilter> topicFilters);
Task UnsubscribeAsync(IEnumerable<string> topics); Task UnsubscribeAsync(IEnumerable<string> topics);
Task PublishAsync(ManagedMqttApplicationMessage applicationMessages); Task PublishAsync(ManagedMqttApplicationMessage applicationMessages);
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <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' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks> <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
<Product /> <Product />
@@ -18,7 +18,7 @@ namespace MQTTnet.Extensions.ManagedClient
{ {
public class ManagedMqttClient : Disposable, IManagedMqttClient public class ManagedMqttClient : Disposable, IManagedMqttClient
{ {
private readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>(); readonly BlockingQueue<ManagedMqttApplicationMessage> _messageQueue = new BlockingQueue<ManagedMqttApplicationMessage>();
/// <summary> /// <summary>
/// The subscriptions are managed in 2 separate buckets: /// 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 /// 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"/>. /// at reconnect and are solely owned by <see cref="MaintainConnectionAsync"/>.
/// </summary> /// </summary>
private readonly Dictionary<string, MqttQualityOfServiceLevel> _reconnectSubscriptions = new Dictionary<string, MqttQualityOfServiceLevel>(); readonly Dictionary<string, MqttQualityOfServiceLevel> _reconnectSubscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>(); readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
private readonly HashSet<string> _unsubscriptions = new HashSet<string>(); readonly HashSet<string> _unsubscriptions = new HashSet<string>();
private readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0); readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
private readonly IMqttClient _mqttClient; readonly IMqttNetScopedLogger _logger;
private readonly IMqttNetScopedLogger _logger;
private readonly AsyncLock _messageQueueLock = new AsyncLock(); readonly AsyncLock _messageQueueLock = new AsyncLock();
private CancellationTokenSource _connectionCancellationToken; CancellationTokenSource _connectionCancellationToken;
private CancellationTokenSource _publishingCancellationToken; CancellationTokenSource _publishingCancellationToken;
private Task _maintainConnectionTask; Task _maintainConnectionTask;
private ManagedMqttClientStorageManager _storageManager; ManagedMqttClientStorageManager _storageManager;
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger) 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)); if (logger == null) throw new ArgumentNullException(nameof(logger));
_logger = logger.CreateScopedLogger(nameof(ManagedMqttClient)); _logger = logger.CreateScopedLogger(nameof(ManagedMqttClient));
} }
public bool IsConnected => _mqttClient.IsConnected; public bool IsConnected => InternalClient.IsConnected;
public bool IsStarted => _connectionCancellationToken != null; public bool IsStarted => _connectionCancellationToken != null;
public IMqttClient InternalClient { get; }
public int PendingApplicationMessagesCount => _messageQueue.Count; public int PendingApplicationMessagesCount => _messageQueue.Count;
public IManagedMqttClientOptions Options { get; private set; } public IManagedMqttClientOptions Options { get; private set; }
public IMqttClientConnectedHandler ConnectedHandler public IMqttClientConnectedHandler ConnectedHandler
{ {
get => _mqttClient.ConnectedHandler; get => InternalClient.ConnectedHandler;
set => _mqttClient.ConnectedHandler = value; set => InternalClient.ConnectedHandler = value;
} }
public IMqttClientDisconnectedHandler DisconnectedHandler public IMqttClientDisconnectedHandler DisconnectedHandler
{ {
get => _mqttClient.DisconnectedHandler; get => InternalClient.DisconnectedHandler;
set => _mqttClient.DisconnectedHandler = value; set => InternalClient.DisconnectedHandler = value;
} }
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler
{ {
get => _mqttClient.ApplicationMessageReceivedHandler; get => InternalClient.ApplicationMessageReceivedHandler;
set => _mqttClient.ApplicationMessageReceivedHandler = value; set => InternalClient.ApplicationMessageReceivedHandler = value;
} }
public IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; } public IApplicationMessageProcessedHandler ApplicationMessageProcessedHandler { get; set; }
@@ -89,14 +93,13 @@ namespace MQTTnet.Extensions.ManagedClient
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options)); if (options.ClientOptions == 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."); if (!_maintainConnectionTask?.IsCompleted ?? false) throw new InvalidOperationException("The managed client is already started.");
Options = options; Options = options;
if (Options.Storage != null) if (options.Storage != null)
{ {
_storageManager = new ManagedMqttClientStorageManager(Options.Storage); _storageManager = new ManagedMqttClientStorageManager(options.Storage);
var messages = await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false); var messages = await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
foreach (var message in messages) 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 = Task.Run(() => MaintainConnectionAsync(cancellationToken), cancellationToken);
_maintainConnectionTask.Forget(_logger); _maintainConnectionTask.RunInBackground(_logger);
_logger.Info("Started"); _logger.Info("Started");
} }
@@ -131,7 +136,7 @@ namespace MQTTnet.Extensions.ManagedClient
public Task PingAsync(CancellationToken cancellationToken) public Task PingAsync(CancellationToken cancellationToken)
{ {
return _mqttClient.PingAsync(cancellationToken); return InternalClient.PingAsync(cancellationToken);
} }
public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken) public async Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
@@ -218,6 +223,7 @@ namespace MQTTnet.Extensions.ManagedClient
_unsubscriptions.Remove(topicFilter.Topic); _unsubscriptions.Remove(topicFilter.Topic);
} }
} }
_subscriptionsQueuedSignal.Release(); _subscriptionsQueuedSignal.Release();
return Task.FromResult(0); return Task.FromResult(0);
@@ -257,13 +263,14 @@ namespace MQTTnet.Extensions.ManagedClient
_messageQueue.Dispose(); _messageQueue.Dispose();
_messageQueueLock.Dispose(); _messageQueueLock.Dispose();
_mqttClient.Dispose(); InternalClient.Dispose();
_subscriptionsQueuedSignal.Dispose(); _subscriptionsQueuedSignal.Dispose();
} }
base.Dispose(disposing); base.Dispose(disposing);
} }
private async Task MaintainConnectionAsync(CancellationToken cancellationToken) async Task MaintainConnectionAsync(CancellationToken cancellationToken)
{ {
try try
{ {
@@ -285,7 +292,14 @@ namespace MQTTnet.Extensions.ManagedClient
{ {
try 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) catch (Exception exception)
{ {
@@ -294,7 +308,9 @@ namespace MQTTnet.Extensions.ManagedClient
_logger.Info("Stopped"); _logger.Info("Stopped");
} }
_reconnectSubscriptions.Clear(); _reconnectSubscriptions.Clear();
lock (_subscriptions) lock (_subscriptions)
{ {
_subscriptions.Clear(); _subscriptions.Clear();
@@ -303,11 +319,11 @@ namespace MQTTnet.Extensions.ManagedClient
} }
} }
private async Task TryMaintainConnectionAsync(CancellationToken cancellationToken) async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
{ {
try try
{ {
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false); var connectionState = await ReconnectIfRequiredAsync(cancellationToken).ConfigureAwait(false);
if (connectionState == ReconnectionResult.NotConnected) if (connectionState == ReconnectionResult.NotConnected)
{ {
StopPublishing(); StopPublishing();
@@ -346,11 +362,11 @@ namespace MQTTnet.Extensions.ManagedClient
} }
} }
private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken) async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
{ {
try try
{ {
while (!cancellationToken.IsCancellationRequested && _mqttClient.IsConnected) while (!cancellationToken.IsCancellationRequested && InternalClient.IsConnected)
{ {
// Peek at the message without dequeueing in order to prevent the // Peek at the message without dequeueing in order to prevent the
// possibility of the queue growing beyond the configured cap. // 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; Exception transmitException = null;
try 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 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; 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) if (message.ApplicationMessage.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{ {
@@ -436,7 +452,7 @@ namespace MQTTnet.Extensions.ManagedClient
catch (Exception exception) catch (Exception exception)
{ {
transmitException = 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 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; var endTime = DateTime.UtcNow + timeout;
while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false)) while (await _subscriptionsQueuedSignal.WaitAsync(GetRemainingTime(endTime), cancellationToken).ConfigureAwait(false))
{ {
List<MqttTopicFilter> subscriptions; List<MqttTopicFilter> subscriptions;
@@ -470,7 +487,7 @@ namespace MQTTnet.Extensions.ManagedClient
continue; 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) foreach (var unsubscription in unsubscriptions)
{ {
@@ -486,12 +503,12 @@ namespace MQTTnet.Extensions.ManagedClient
{ {
if (unsubscriptions.Any()) if (unsubscriptions.Any())
{ {
await _mqttClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false); await InternalClient.UnsubscribeAsync(unsubscriptions.ToArray()).ConfigureAwait(false);
} }
if (subscriptions.Any()) if (subscriptions.Any())
{ {
await _mqttClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false); await InternalClient.SubscribeAsync(subscriptions.ToArray()).ConfigureAwait(false);
} }
} }
catch (Exception exception) catch (Exception exception)
@@ -501,7 +518,7 @@ namespace MQTTnet.Extensions.ManagedClient
} }
} }
private async Task PublishReconnectSubscriptionsAsync() async Task PublishReconnectSubscriptionsAsync()
{ {
_logger.Info("Publishing subscriptions at reconnect"); _logger.Info("Publishing subscriptions at reconnect");
@@ -510,7 +527,7 @@ namespace MQTTnet.Extensions.ManagedClient
if (_reconnectSubscriptions.Any()) if (_reconnectSubscriptions.Any())
{ {
var subscriptions = _reconnectSubscriptions.Select(i => new MqttTopicFilter { Topic = i.Key, QualityOfServiceLevel = i.Value }); 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) 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."); _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; return ReconnectionResult.StillConnected;
} }
try 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; return result.IsSessionPresent ? ReconnectionResult.Recovered : ReconnectionResult.Reconnected;
} }
catch (Exception exception) catch (Exception exception)
@@ -554,34 +571,47 @@ namespace MQTTnet.Extensions.ManagedClient
} }
} }
private void StartPublishing() void StartPublishing()
{ {
if (_publishingCancellationToken != null) if (_publishingCancellationToken != null)
{ {
StopPublishing(); StopPublishing();
} }
var cts = new CancellationTokenSource(); var cancellationTokenSource = new CancellationTokenSource();
_publishingCancellationToken = cts; 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); try
_publishingCancellationToken?.Dispose(); {
_publishingCancellationToken = null; _publishingCancellationToken?.Cancel(false);
}
finally
{
_publishingCancellationToken?.Dispose();
_publishingCancellationToken = null;
}
} }
private void StopMaintainingConnection() void StopMaintainingConnection()
{ {
_connectionCancellationToken?.Cancel(false); try
_connectionCancellationToken?.Dispose(); {
_connectionCancellationToken = null; _connectionCancellationToken?.Cancel(false);
}
finally
{
_connectionCancellationToken?.Dispose();
_connectionCancellationToken = null;
}
} }
private TimeSpan GetRemainingTime(DateTime endTime) static TimeSpan GetRemainingTime(DateTime endTime)
{ {
var remainingTime = endTime - DateTime.UtcNow; var remainingTime = endTime - DateTime.UtcNow;
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime; return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
@@ -1,5 +1,4 @@
using System; using System;
using MQTTnet.Client;
using MQTTnet.Client.Options; using MQTTnet.Client.Options;
using MQTTnet.Server; using MQTTnet.Server;
@@ -1,5 +1,4 @@
using System; using System;
using MQTTnet.Client;
using MQTTnet.Client.Options; using MQTTnet.Client.Options;
using MQTTnet.Server; using MQTTnet.Server;
@@ -7,8 +6,8 @@ namespace MQTTnet.Extensions.ManagedClient
{ {
public class ManagedMqttClientOptionsBuilder public class ManagedMqttClientOptionsBuilder
{ {
private readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions(); readonly ManagedMqttClientOptions _options = new ManagedMqttClientOptions();
private MqttClientOptionsBuilder _clientOptionsBuilder; MqttClientOptionsBuilder _clientOptionsBuilder;
public ManagedMqttClientOptionsBuilder WithMaxPendingMessages(int value) public ManagedMqttClientOptionsBuilder WithMaxPendingMessages(int value)
{ {
@@ -69,7 +68,7 @@ namespace MQTTnet.Extensions.ManagedClient
options(_clientOptionsBuilder); options(_clientOptionsBuilder);
return this; return this;
} }
public ManagedMqttClientOptions Build() public ManagedMqttClientOptions Build()
{ {
if (_clientOptionsBuilder != null) 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"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <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' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks> <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
<Product /> <Product />
+6 -11
View File
@@ -11,12 +11,12 @@ using System.Threading.Tasks;
namespace MQTTnet.Extensions.Rpc 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[]>>(); readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
private readonly IMqttClient _mqttClient; readonly IMqttClient _mqttClient;
private readonly IMqttRpcClientOptions _options; readonly IMqttRpcClientOptions _options;
private readonly RpcAwareApplicationMessageReceivedHandler _applicationMessageReceivedHandler; readonly RpcAwareApplicationMessageReceivedHandler _applicationMessageReceivedHandler;
[Obsolete("Use MqttRpcClient(IMqttClient mqttClient, IMqttRpcClientOptions options).")] [Obsolete("Use MqttRpcClient(IMqttClient mqttClient, IMqttRpcClientOptions options).")]
public MqttRpcClient(IMqttClient mqttClient) : this(mqttClient, new MqttRpcClientOptions()) 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 == 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)) if (!(_mqttClient.ApplicationMessageReceivedHandler is RpcAwareApplicationMessageReceivedHandler))
{ {
throw new InvalidOperationException("The application message received handler was modified."); 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)) if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var tcs))
{ {
@@ -2,7 +2,7 @@
namespace MQTTnet.Extensions.Rpc.Options namespace MQTTnet.Extensions.Rpc.Options
{ {
public class MqttRpcClientOptions : IMqttRpcClientOptions public sealed class MqttRpcClientOptions : IMqttRpcClientOptions
{ {
public IMqttRpcClientTopicGenerationStrategy TopicGenerationStrategy { get; set; } = new DefaultMqttRpcClientTopicGenerationStrategy(); public IMqttRpcClientTopicGenerationStrategy TopicGenerationStrategy { get; set; } = new DefaultMqttRpcClientTopicGenerationStrategy();
} }
@@ -3,7 +3,7 @@ using System;
namespace MQTTnet.Extensions.Rpc.Options namespace MQTTnet.Extensions.Rpc.Options
{ {
public class MqttRpcClientOptionsBuilder public sealed class MqttRpcClientOptionsBuilder
{ {
IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy(); IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy();
@@ -1,6 +1,6 @@
namespace MQTTnet.Extensions.Rpc.Options namespace MQTTnet.Extensions.Rpc.Options
{ {
public class MqttRpcTopicPair public sealed class MqttRpcTopicPair
{ {
public string RequestTopic { get; set; } public string RequestTopic { get; set; }
@@ -2,10 +2,15 @@
namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
{ {
public class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy public sealed class DefaultMqttRpcClientTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
{ {
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context) 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 requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{context.MethodName}";
var responseTopic = requestTopic + "/response"; var responseTopic = requestTopic + "/response";
@@ -3,10 +3,18 @@ using MQTTnet.Protocol;
namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration namespace MQTTnet.Extensions.Rpc.Options.TopicGeneration
{ {
public class TopicGenerationContext public sealed class TopicGenerationContext
{ {
public string MethodName { get; set; } 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 MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
public IMqttClient MqttClient { get; set; } public IMqttClient MqttClient { get; set; }
@@ -4,9 +4,9 @@ using MQTTnet.Client.Receiving;
namespace MQTTnet.Extensions.Rpc 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( public RpcAwareApplicationMessageReceivedHandler(
IMqttApplicationMessageReceivedHandler originalHandler, IMqttApplicationMessageReceivedHandler originalHandler,
@@ -18,14 +18,14 @@ namespace MQTTnet.Extensions.Rpc
public IMqttApplicationMessageReceivedHandler OriginalHandler { get; } public IMqttApplicationMessageReceivedHandler OriginalHandler { get; }
public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs) public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{ {
if (OriginalHandler != null) 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"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <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' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks> <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' AND '$(SolutionName)' != 'MQTTnet.noUWP' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
<Product /> <Product />
@@ -16,14 +16,14 @@ using WebSocket4Net;
namespace MQTTnet.Extensions.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; readonly IMqttClientOptions _clientOptions;
private readonly MqttClientWebSocketOptions _webSocketOptions; readonly MqttClientWebSocketOptions _webSocketOptions;
private WebSocket _webSocket; WebSocket _webSocket;
public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions) public WebSocket4NetMqttChannel(IMqttClientOptions clientOptions, MqttClientWebSocketOptions webSocketOptions)
{ {
@@ -166,12 +166,12 @@ namespace MQTTnet.Extensions.WebSocket4Net
_webSocket = null; _webSocket = null;
} }
private void OnError(object sender, ErrorEventArgs e) static void OnError(object sender, ErrorEventArgs e)
{ {
System.Diagnostics.Debug.Write(e.Exception.ToString()); 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) 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.Error += OnError;
_webSocket.DataReceived += OnDataReceived; _webSocket.DataReceived += OnDataReceived;
@@ -201,7 +201,9 @@ namespace MQTTnet.Extensions.WebSocket4Net
_webSocket.Opened += SuccessHandler; _webSocket.Opened += SuccessHandler;
_webSocket.Error += ErrorHandler; _webSocket.Error += ErrorHandler;
#pragma warning disable AsyncFixer02 // Long-running or blocking operations inside an async method
_webSocket.Open(); _webSocket.Open();
#pragma warning restore AsyncFixer02 // Long-running or blocking operations inside an async method
var exception = await MqttTaskTimeout.WaitAsync(c => var exception = await MqttTaskTimeout.WaitAsync(c =>
{ {
@@ -7,7 +7,7 @@ using System;
namespace MQTTnet.Extensions.WebSocket4Net namespace MQTTnet.Extensions.WebSocket4Net
{ {
public class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory public sealed class WebSocket4NetMqttClientAdapterFactory : IMqttClientAdapterFactory
{ {
readonly IMqttNetLogger _logger; readonly IMqttNetLogger _logger;
@@ -23,14 +23,22 @@ namespace MQTTnet.Extensions.WebSocket4Net
switch (options.ChannelOptions) switch (options.ChannelOptions)
{ {
case MqttClientTcpOptions _: 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: 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: 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"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFrameworks>net5.0</TargetFrameworks>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel> <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<AssemblyName>MQTTnet.Server</AssemblyName> <AssemblyName>MQTTnet.Server</AssemblyName>
<RootNamespace>MQTTnet.Server</RootNamespace> <RootNamespace>MQTTnet.Server</RootNamespace>
@@ -39,12 +39,19 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="IronPython" Version="2.7.10" /> <PackageReference Include="IronPython" Version="2.7.11" />
<PackageReference Include="IronPython.StdLib" Version="2.7.10" /> <PackageReference Include="IronPython.StdLib" Version="2.7.11" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.5" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" /> <PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.5.1" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.6.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.5.1" /> <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>
<ItemGroup> <ItemGroup>
@@ -212,9 +212,15 @@ namespace MQTTnet.Server.Mqtt
// Configure encrypted connections // Configure encrypted connections
if (_settings.EncryptedTcpEndPoint.Enabled) if (_settings.EncryptedTcpEndPoint.Enabled)
{ {
#if NETCOREAPP3_1 || NET5_0
options
.WithEncryptedEndpoint()
.WithEncryptionSslProtocol(SslProtocols.Tls13);
#else
options options
.WithEncryptedEndpoint() .WithEncryptedEndpoint()
.WithEncryptionSslProtocol(SslProtocols.Tls12); .WithEncryptionSslProtocol(SslProtocols.Tls12);
#endif
if (!string.IsNullOrEmpty(_settings.EncryptedTcpEndPoint?.Certificate?.Path)) if (!string.IsNullOrEmpty(_settings.EncryptedTcpEndPoint?.Certificate?.Path))
{ {
+8 -4
View File
@@ -116,10 +116,15 @@ namespace MQTTnet.Server.Web
{ {
var securityScheme = new OpenApiSecurityScheme var securityScheme = new OpenApiSecurityScheme
{ {
Scheme = "Basic", Scheme = "basic",
Name = HeaderNames.Authorization, Name = HeaderNames.Authorization,
Type = SecuritySchemeType.Http, Type = SecuritySchemeType.Http,
In = ParameterLocation.Header In = ParameterLocation.Header,
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Swagger"
}
}; };
c.AddSecurityDefinition("Swagger", securityScheme); c.AddSecurityDefinition("Swagger", securityScheme);
@@ -181,8 +186,7 @@ namespace MQTTnet.Server.Web
var webSocketOptions = new WebSocketOptions var webSocketOptions = new WebSocketOptions
{ {
KeepAliveInterval = TimeSpan.FromSeconds(mqttSettings.WebSocketEndPoint.KeepAliveInterval), KeepAliveInterval = TimeSpan.FromSeconds(mqttSettings.WebSocketEndPoint.KeepAliveInterval)
ReceiveBufferSize = mqttSettings.WebSocketEndPoint.ReceiveBufferSize
}; };
if (mqttSettings.WebSocketEndPoint.AllowedOrigins?.Any() == true) if (mqttSettings.WebSocketEndPoint.AllowedOrigins?.Any() == true)
@@ -21,17 +21,15 @@ namespace MQTTnet.Adapter
long BytesReceived { get; } long BytesReceived { get; }
Action ReadingPacketStartedCallback { get; set; } bool IsReadingPacket { get; }
Action ReadingPacketCompletedCallback { get; set; }
Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken); Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
Task DisconnectAsync(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(); void ResetStatistics();
} }
+189 -95
View File
@@ -11,6 +11,7 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Adapter namespace MQTTnet.Adapter
{ {
@@ -19,26 +20,26 @@ namespace MQTTnet.Adapter
const uint ErrorOperationAborted = 0x800703E3; const uint ErrorOperationAborted = 0x800703E3;
const int ReadBufferSize = 4096; const int ReadBufferSize = 4096;
readonly byte[] _singleByteBuffer = new byte[1];
readonly byte[] _fixedHeaderBuffer = new byte[2];
readonly MqttPacketInspectorHandler _packetInspectorHandler;
readonly IMqttNetScopedLogger _logger; readonly IMqttNetScopedLogger _logger;
readonly IMqttChannel _channel; readonly IMqttChannel _channel;
readonly MqttPacketReader _packetReader;
readonly byte[] _fixedHeaderBuffer = new byte[2];
readonly AsyncLock _syncRoot = new AsyncLock(); readonly AsyncLock _syncRoot = new AsyncLock();
long _bytesReceived; long _bytesReceived;
long _bytesSent; 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)); _channel = channel ?? throw new ArgumentNullException(nameof(channel));
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter)); 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)); _logger = logger.CreateScopedLogger(nameof(MqttChannelAdapter));
} }
@@ -51,10 +52,10 @@ namespace MQTTnet.Adapter
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; } public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
public long BytesSent => Interlocked.Read(ref _bytesSent); public long BytesSent => Interlocked.Read(ref _bytesSent);
public long BytesReceived => Interlocked.Read(ref _bytesReceived); public long BytesReceived => Interlocked.Read(ref _bytesReceived);
public Action ReadingPacketStartedCallback { get; set; } public bool IsReadingPacket { get; private set; }
public Action ReadingPacketCompletedCallback { get; set; }
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken) public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{ {
@@ -79,7 +80,7 @@ namespace MQTTnet.Adapter
throw; throw;
} }
WrapException(exception); WrapAndThrowException(exception);
} }
} }
@@ -107,97 +108,78 @@ namespace MQTTnet.Adapter
throw; 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(); cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed(); ThrowIfDisposed();
try try
{ {
using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false)) _packetInspectorHandler.BeginReceivePacket();
{
// Check for cancellation here again because "WaitAsync" might take some time.
cancellationToken.ThrowIfCancellationRequested();
try var receivedPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
{ if (receivedPacket == null || cancellationToken.IsCancellationRequested)
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)
{ {
return null; return null;
} }
Interlocked.Add(ref _bytesSent, receivedMqttPacket.TotalLength); _packetInspectorHandler.EndReceivePacket();
Interlocked.Add(ref _bytesSent, receivedPacket.TotalLength);
if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown) if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown)
{ {
PacketFormatterAdapter.DetectProtocolVersion(receivedMqttPacket); PacketFormatterAdapter.DetectProtocolVersion(receivedPacket);
} }
var packet = PacketFormatterAdapter.Decode(receivedMqttPacket); var packet = PacketFormatterAdapter.Decode(receivedPacket);
if (packet == null) if (packet == null)
{ {
throw new MqttProtocolViolationException("Received malformed packet."); 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; return packet;
} }
@@ -214,7 +196,7 @@ namespace MQTTnet.Adapter
throw; throw;
} }
WrapException(exception); WrapAndThrowException(exception);
} }
return null; return null;
@@ -239,31 +221,38 @@ namespace MQTTnet.Adapter
async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken) 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) if (cancellationToken.IsCancellationRequested)
{ {
return null; return null;
} }
if (readFixedHeaderResult.ConnectionClosed)
{
return null;
}
try try
{ {
if (readFixedHeaderResult.ConnectionClosed) IsReadingPacket = true;
{
return null;
}
ReadingPacketStartedCallback?.Invoke();
var fixedHeader = readFixedHeaderResult.FixedHeader; var fixedHeader = readFixedHeaderResult.FixedHeader;
if (fixedHeader.RemainingLength == 0) 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 bodyOffset = 0;
var chunkSize = Math.Min(ReadBufferSize, fixedHeader.RemainingLength); var chunkSize = Math.Min(ReadBufferSize, bodyLength);
do do
{ {
@@ -286,25 +275,126 @@ namespace MQTTnet.Adapter
} }
bodyOffset += readBytes; 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); return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength);
} }
finally 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) static bool IsWrappedException(Exception exception)
{ {
return exception is OperationCanceledException || return exception is OperationCanceledException ||
exception is MqttCommunicationTimedOutException || 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) if (exception is IOException && exception.InnerException is SocketException innerException)
{ {
@@ -313,11 +403,15 @@ namespace MQTTnet.Adapter
if (exception is SocketException socketException) if (exception is SocketException socketException)
{ {
if (socketException.SocketErrorCode == SocketError.ConnectionAborted || if (socketException.SocketErrorCode == SocketError.OperationAborted)
socketException.SocketErrorCode == SocketError.OperationAborted)
{ {
throw new OperationCanceledException(); throw new OperationCanceledException();
} }
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
{
throw new MqttCommunicationException(socketException);
}
} }
if (exception is COMException comException) 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 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; FixedHeader = fixedHeader;
Body = body; BodyReader = bodyReader ?? throw new ArgumentNullException(nameof(bodyReader));
TotalLength = totalLength; TotalLength = totalLength;
} }
public byte FixedHeader { get; set; } public byte FixedHeader { get; }
public IMqttPacketBodyReader Body { get; } public IMqttPacketBodyReader BodyReader { get; }
public int TotalLength { get; } public int TotalLength { get; }
} }
@@ -1,11 +1,16 @@
using System.Collections.Generic; using System.Collections.Generic;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Client.Connecting namespace MQTTnet.Client.Connecting
{ {
// TODO: Consider renaming this to _MqttClientConnectResult_ // TODO: Consider renaming this to _MqttClientConnectResult_
public class MqttClientAuthenticateResult public class MqttClientAuthenticateResult
{ {
/// <summary>
/// Gets or sets the result code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientConnectResultCode ResultCode { get; set; } public MqttClientConnectResultCode ResultCode { get; set; }
public bool IsSessionPresent { get; set; } public bool IsSessionPresent { get; set; }
@@ -16,16 +21,34 @@ namespace MQTTnet.Client.Connecting
public string AssignedClientIdentifier { get; set; } public string AssignedClientIdentifier { get; set; }
/// <summary>
/// Gets or sets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; set; } public string AuthenticationMethod { get; set; }
/// <summary>
/// Gets or sets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; set; } public byte[] AuthenticationData { get; set; }
public uint? MaximumPacketSize { 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 string ReasonString { get; set; }
public ushort? ReceiveMaximum { 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 string ResponseInformation { get; set; }
public ushort? TopicAliasMaximum { get; set; } public ushort? TopicAliasMaximum { get; set; }
@@ -36,10 +59,25 @@ namespace MQTTnet.Client.Connecting
public uint? SessionExpiryInterval { get; set; } public uint? SessionExpiryInterval { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the subscription identifiers are available or not.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? SubscriptionIdentifiersAvailable { get; set; } 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; } 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; } public List<MqttUserProperty> UserProperties { get; set; }
} }
} }
@@ -9,6 +9,10 @@ namespace MQTTnet.Client.Connecting
AuthenticateResult = authenticateResult ?? throw new ArgumentNullException(nameof(authenticateResult)); AuthenticateResult = authenticateResult ?? throw new ArgumentNullException(nameof(authenticateResult));
} }
/// <summary>
/// Gets the authentication result.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientAuthenticateResult AuthenticateResult { get; } public MqttClientAuthenticateResult AuthenticateResult { get; }
} }
} }
@@ -1,9 +1,17 @@
namespace MQTTnet.Client.Disconnecting 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; 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; } public string ReasonString { get; set; }
} }
} }
@@ -3,22 +3,35 @@ using MQTTnet.Client.Connecting;
namespace MQTTnet.Client.Disconnecting 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; ClientWasConnected = clientWasConnected;
Exception = exception; Exception = exception;
AuthenticateResult = authenticateResult; AuthenticateResult = authenticateResult;
ReasonCode = reasonCode; Reason = reason;
ReasonCode = reason;
} }
public bool ClientWasConnected { get; } public bool ClientWasConnected { get; }
public Exception Exception { get; } public Exception Exception { get; }
/// <summary>
/// Gets the authentication result.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientAuthenticateResult AuthenticateResult { get; } 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)); Client = client ?? throw new ArgumentNullException(nameof(client));
} }
/// <summary>
/// Gets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttAuthenticateReasonCode ReasonCode { get; } public MqttAuthenticateReasonCode ReasonCode { get; }
/// <summary>
/// Gets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; } public string ReasonString { get; }
/// <summary>
/// Gets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; } public string AuthenticationMethod { get; }
/// <summary>
/// Gets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; } 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 List<MqttUserProperty> UserProperties { get; }
public IMqttClient Client { get; } public IMqttClient Client { get; }
@@ -6,12 +6,33 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange
{ {
public class MqttExtendedAuthenticationExchangeData public class MqttExtendedAuthenticationExchangeData
{ {
/// <summary>
/// Gets or sets the reason code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttAuthenticateReasonCode ReasonCode { get; set; } public MqttAuthenticateReasonCode ReasonCode { get; set; }
/// <summary>
/// Gets or sets the reason string.
/// Hint: MQTT 5 feature only.
/// </summary>
public string ReasonString { get; set; } 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; } 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; } public List<MqttUserProperty> UserProperties { get; }
} }
} }
+20
View File
@@ -13,18 +13,38 @@ namespace MQTTnet.Client
public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
{ {
bool IsConnected { get; } bool IsConnected { get; }
IMqttClientOptions Options { 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; } 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; } IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
Task<MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken); Task<MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken);
Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken); Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken);
Task PingAsync(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 SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken);
Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken); Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken);
Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions 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.Diagnostics;
using MQTTnet.LowLevelClient; using MQTTnet.LowLevelClient;
+122 -71
View File
@@ -17,6 +17,7 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Implementations;
namespace MQTTnet.Client namespace MQTTnet.Client
{ {
@@ -42,6 +43,7 @@ namespace MQTTnet.Client
bool _cleanDisconnectInitiated; bool _cleanDisconnectInitiated;
long _isDisconnectPending; long _isDisconnectPending;
bool _isConnected; bool _isConnected;
MqttClientDisconnectReason _disconnectReason;
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger) public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
{ {
@@ -77,7 +79,7 @@ namespace MQTTnet.Client
Options = options; Options = options;
_packetIdentifierProvider.Reset(); _packetIdentifierProvider.Reset();
_packetDispatcher.Reset(); _packetDispatcher.Cancel();
_backgroundCancellationTokenSource = new CancellationTokenSource(); _backgroundCancellationTokenSource = new CancellationTokenSource();
var backgroundCancellationToken = _backgroundCancellationTokenSource.Token; var backgroundCancellationToken = _backgroundCancellationTokenSource.Token;
@@ -88,8 +90,8 @@ namespace MQTTnet.Client
using (var combined = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken)) using (var combined = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken))
{ {
_logger.Verbose($"Trying to connect with server '{options.ChannelOptions}' (Timeout={options.CommunicationTimeout})."); _logger.Verbose("Trying to connect with server '{0}' (Timeout={1}).", options.ChannelOptions, options.CommunicationTimeout);
await _adapter.ConnectAsync(options.CommunicationTimeout, combined.Token).ConfigureAwait(false); await adapter.ConnectAsync(options.CommunicationTimeout, combined.Token).ConfigureAwait(false);
_logger.Verbose("Connection with server established."); _logger.Verbose("Connection with server established.");
_publishPacketReceiverQueue = new AsyncQueue<MqttPublishPacket>(); _publishPacketReceiverQueue = new AsyncQueue<MqttPublishPacket>();
@@ -122,6 +124,8 @@ namespace MQTTnet.Client
} }
catch (Exception exception) catch (Exception exception)
{ {
_disconnectReason = MqttClientDisconnectReason.UnspecifiedError;
_logger.Error(exception, "Error while connecting with server."); _logger.Error(exception, "Error while connecting with server.");
if (!DisconnectIsPending()) if (!DisconnectIsPending())
@@ -139,11 +143,17 @@ namespace MQTTnet.Client
ThrowIfDisposed(); ThrowIfDisposed();
if (DisconnectIsPending())
{
return;
}
try try
{ {
_disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
_cleanDisconnectInitiated = true; _cleanDisconnectInitiated = true;
if (IsConnected) if (_isConnected)
{ {
var disconnectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(options); var disconnectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(options);
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false); await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
@@ -151,10 +161,7 @@ namespace MQTTnet.Client
} }
finally 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) 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(); ThrowIfDisposed();
ThrowIfNotConnected(); ThrowIfNotConnected();
@@ -245,24 +252,27 @@ namespace MQTTnet.Client
void Cleanup() void Cleanup()
{ {
_backgroundCancellationTokenSource?.Cancel(false); try
_backgroundCancellationTokenSource?.Dispose(); {
_backgroundCancellationTokenSource = null; _backgroundCancellationTokenSource?.Cancel(false);
}
finally
{
_backgroundCancellationTokenSource?.Dispose();
_backgroundCancellationTokenSource = null;
_publishPacketReceiverQueue?.Dispose(); _publishPacketReceiverQueue?.Dispose();
_publishPacketReceiverQueue = null; _publishPacketReceiverQueue = null;
_adapter?.Dispose(); _adapter?.Dispose();
}
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {
Cleanup(); Cleanup();
DisconnectedHandler = null;
} }
base.Dispose(disposing); base.Dispose(disposing);
@@ -302,8 +312,7 @@ namespace MQTTnet.Client
async Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult) async Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult)
{ {
var clientWasConnected = IsConnected; var clientWasConnected = _isConnected;
var reasonCode = MqttClientDisconnectReason.NormalDisconnection;
TryInitiateDisconnect(); TryInitiateDisconnect();
_isConnected = false; _isConnected = false;
@@ -321,7 +330,6 @@ namespace MQTTnet.Client
catch (Exception adapterException) catch (Exception adapterException)
{ {
_logger.Warning(adapterException, "Error while disconnecting from adapter."); _logger.Warning(adapterException, "Error while disconnecting from adapter.");
reasonCode = MqttClientDisconnectReason.UnspecifiedError;
} }
try try
@@ -337,7 +345,6 @@ namespace MQTTnet.Client
catch (Exception e) catch (Exception e)
{ {
_logger.Warning(e, "Error while waiting for internal tasks."); _logger.Warning(e, "Error while waiting for internal tasks.");
reasonCode = MqttClientDisconnectReason.UnspecifiedError;
} }
finally finally
{ {
@@ -351,7 +358,7 @@ namespace MQTTnet.Client
{ {
// This handler must be executed in a new thread because otherwise a dead lock may happen // This handler must be executed in a new thread because otherwise a dead lock may happen
// when trying to reconnect in that handler etc. // 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 try
{ {
if (_backgroundCancellationTokenSource?.IsCancellationRequested == true)
{
return;
}
_backgroundCancellationTokenSource?.Cancel(false); _backgroundCancellationTokenSource?.Cancel(false);
} }
catch (Exception exception) catch (Exception exception)
@@ -382,17 +384,17 @@ namespace MQTTnet.Client
_sendTracker.Restart(); _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 async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttBasePacket
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
ushort identifier = 0; 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)) using (var packetAwaiter = _packetDispatcher.AddAwaiter<TResponsePacket>(identifier))
@@ -400,12 +402,11 @@ namespace MQTTnet.Client
try try
{ {
_sendTracker.Restart(); _sendTracker.Restart();
await _adapter.SendPacketAsync(requestPacket, cancellationToken).ConfigureAwait(false);
await _adapter.SendPacketAsync(requestPacket, Options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
} }
catch (Exception e) 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(); packetAwaiter.Cancel();
} }
@@ -417,7 +418,7 @@ namespace MQTTnet.Client
{ {
if (exception is MqttCommunicationTimedOutException) 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; throw;
@@ -437,10 +438,10 @@ namespace MQTTnet.Client
{ {
// Values described here: [MQTT-3.1.2-24]. // Values described here: [MQTT-3.1.2-24].
var waitTime = keepAlivePeriod - _sendTracker.Elapsed; var waitTime = keepAlivePeriod - _sendTracker.Elapsed;
if (waitTime <= TimeSpan.Zero) 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 // 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) if (exception is OperationCanceledException)
{ {
return;
} }
else if (exception is MqttCommunicationException) else if (exception is MqttCommunicationException)
{ {
@@ -488,7 +490,7 @@ namespace MQTTnet.Client
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false); var packet = await _adapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -550,32 +552,25 @@ namespace MQTTnet.Client
{ {
EnqueueReceivedPublishPacket(publishPacket); EnqueueReceivedPublishPacket(publishPacket);
} }
else if (packet is MqttPubRecPacket pubRecPacket)
{
await ProcessReceivedPubRecPacket(pubRecPacket, cancellationToken).ConfigureAwait(false);
}
else if (packet is MqttPubRelPacket pubRelPacket) else if (packet is MqttPubRelPacket pubRelPacket)
{ {
await SendAsync(new MqttPubCompPacket await ProcessReceivedPubRelPacket(pubRelPacket, cancellationToken).ConfigureAwait(false);
{
PacketIdentifier = pubRelPacket.PacketIdentifier,
ReasonCode = MqttPubCompReasonCode.Success
}, cancellationToken).ConfigureAwait(false);
} }
else if (packet is MqttPingReqPacket) 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. await ProcessReceivedDisconnectPacket(disconnectPacket).ConfigureAwait(false);
_packetDispatcher.Dispatch(packet);
await DisconnectAsync(null, cancellationToken).ConfigureAwait(false);
} }
else if (packet is MqttAuthPacket authPacket) else if (packet is MqttAuthPacket authPacket)
{ {
var extendedAuthenticationExchangeHandler = Options.ExtendedAuthenticationExchangeHandler; await ProcessReceivedAuthPacket(authPacket).ConfigureAwait(false);
if (extendedAuthenticationExchangeHandler != null)
{
await extendedAuthenticationExchangeHandler.HandleRequestAsync(new MqttExtendedAuthenticationExchangeContext(authPacket, this)).ConfigureAwait(false);
}
} }
else else
{ {
@@ -618,7 +613,7 @@ namespace MQTTnet.Client
} }
catch (Exception exception) 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 try
{ {
var publishPacketDequeueResult = await _publishPacketReceiverQueue.TryDequeueAsync(cancellationToken); var publishPacketDequeueResult = await _publishPacketReceiverQueue.TryDequeueAsync(cancellationToken).ConfigureAwait(false);
if (!publishPacketDequeueResult.IsSuccess) if (!publishPacketDequeueResult.IsSuccess)
{ {
return; return;
@@ -643,26 +637,28 @@ namespace MQTTnet.Client
} }
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) 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 await SendAsync(new MqttPubAckPacket
{ {
PacketIdentifier = publishPacket.PacketIdentifier, PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubAckReasonCode.Success ReasonCode = (MqttPubAckReasonCode)eventArgs.ReasonCode
}, cancellationToken).ConfigureAwait(false); }, cancellationToken).ConfigureAwait(false);
} }
} }
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) 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, PacketIdentifier = publishPacket.PacketIdentifier,
ReasonCode = MqttPubRecReasonCode.Success ReasonCode = (MqttPubRecReasonCode)eventArgs.ReasonCode
}; }, cancellationToken).ConfigureAwait(false);
await SendAsync(pubRecPacket, cancellationToken).ConfigureAwait(false);
} }
} }
else else
@@ -670,6 +666,9 @@ namespace MQTTnet.Client
throw new MqttProtocolViolationException("Received a not supported QoS level."); throw new MqttProtocolViolationException("Received a not supported QoS level.");
} }
} }
catch (OperationCanceledException)
{
}
catch (Exception exception) catch (Exception exception)
{ {
_logger.Error(exception, "Error while handling application message."); _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) async Task<MqttClientPublishResult> PublishAtMostOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{ {
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier] // 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); 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 applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage);
var handler = ApplicationMessageReceivedHandler; var handler = ApplicationMessageReceivedHandler;
if (handler != null) if (handler != null)
{ {
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage);
await handler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false); await handler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
return !eventArgs.ProcessingFailed;
} }
return true; return eventArgs;
} }
async Task WaitForTaskAsync(Task task, Task sender) async Task WaitForTaskAsync(Task task, Task sender)
@@ -756,6 +805,8 @@ namespace MQTTnet.Client
bool DisconnectIsPending() 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; 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)); 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) public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, params MqttTopicFilter[] topicFilters)
@@ -176,13 +190,6 @@ namespace MQTTnet.Client
return client.ConnectAsync(options, CancellationToken.None); 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) public static Task SendExtendedAuthenticationExchangeDataAsync(this IMqttClient client, MqttExtendedAuthenticationExchangeData data)
{ {
if (client == null) throw new ArgumentNullException(nameof(client)); if (client == null) throw new ArgumentNullException(nameof(client));
@@ -1,9 +1,10 @@
namespace MQTTnet.Client namespace MQTTnet.Client
{ {
public class MqttPacketIdentifierProvider public sealed class MqttPacketIdentifierProvider
{ {
private readonly object _syncRoot = new object(); readonly object _syncRoot = new object();
private ushort _value;
ushort _value;
public void Reset() public void Reset()
{ {
@@ -3,31 +3,112 @@ using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Client.Options namespace MQTTnet.Client.Options
{ {
public interface IMqttClientOptions 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; } 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; } bool CleanSession { get; }
IMqttClientCredentials Credentials { get; } IMqttClientCredentials Credentials { get; }
IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; } IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; }
MqttProtocolVersion ProtocolVersion { get; } MqttProtocolVersion ProtocolVersion { get; }
IMqttClientChannelOptions ChannelOptions { get; } IMqttClientChannelOptions ChannelOptions { get; }
TimeSpan CommunicationTimeout { 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; } 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; } 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; } uint? WillDelayInterval { get; }
/// <summary>
/// Gets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
string AuthenticationMethod { get; } string AuthenticationMethod { get; }
/// <summary>
/// Gets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
byte[] AuthenticationData { get; } byte[] AuthenticationData { get; }
uint? MaximumPacketSize { get; } uint? MaximumPacketSize { get; }
/// <summary>
/// Gets the receive maximum.
/// This gives the maximum length of the receive messages.
/// </summary>
ushort? ReceiveMaximum { get; } ushort? ReceiveMaximum { get; }
/// <summary>
/// Gets the request problem information.
/// Hint: MQTT 5 feature only.
/// </summary>
bool? RequestProblemInformation { get; } bool? RequestProblemInformation { get; }
/// <summary>
/// Gets the request response information.
/// Hint: MQTT 5 feature only.
/// </summary>
bool? RequestResponseInformation { get; } 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; } uint? SessionExpiryInterval { get; }
/// <summary>
/// Gets the topic alias maximum.
/// This gives the maximum length of the topic alias.
/// </summary>
ushort? TopicAliasMaximum { get; } 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; } List<MqttUserProperty> UserProperties { get; set; }
IMqttPacketInspector PacketInspector { get; set; }
} }
} }
@@ -3,33 +3,112 @@ using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Client.Options namespace MQTTnet.Client.Options
{ {
public class MqttClientOptions : IMqttClientOptions 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"); 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 bool CleanSession { get; set; } = true;
public IMqttClientCredentials Credentials { get; set; } public IMqttClientCredentials Credentials { get; set; }
public IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; set; } public IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; set; }
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311; public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
public IMqttClientChannelOptions ChannelOptions { get; set; } public IMqttClientChannelOptions ChannelOptions { get; set; }
public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10); 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); 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; } 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; } public uint? WillDelayInterval { get; set; }
/// <summary>
/// Gets or sets the authentication method.
/// Hint: MQTT 5 feature only.
/// </summary>
public string AuthenticationMethod { get; set; } public string AuthenticationMethod { get; set; }
/// <summary>
/// Gets or sets the authentication data.
/// Hint: MQTT 5 feature only.
/// </summary>
public byte[] AuthenticationData { get; set; } public byte[] AuthenticationData { get; set; }
public uint? MaximumPacketSize { 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; } public ushort? ReceiveMaximum { get; set; }
/// <summary>
/// Gets or sets the request problem information.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? RequestProblemInformation { get; set; } public bool? RequestProblemInformation { get; set; }
/// <summary>
/// Gets or sets the request response information.
/// Hint: MQTT 5 feature only.
/// </summary>
public bool? RequestResponseInformation { get; set; } 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; } 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; } 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 List<MqttUserProperty> UserProperties { get; set; }
public IMqttPacketInspector PacketInspector { get; set; }
} }
} }
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MQTTnet.Diagnostics.PacketInspection;
namespace MQTTnet.Client.Options namespace MQTTnet.Client.Options
{ {
@@ -256,6 +257,12 @@ namespace MQTTnet.Client.Options
return this; return this;
} }
public MqttClientOptionsBuilder WithPacketInspector(IMqttPacketInspector packetInspector)
{
_options.PacketInspector = packetInspector;
return this;
}
public IMqttClientOptions Build() public IMqttClientOptions Build()
{ {
if (_tcpOptions == null && _webSocketOptions == null) if (_tcpOptions == null && _webSocketOptions == null)
@@ -280,7 +287,9 @@ namespace MQTTnet.Client.Options
#pragma warning disable CS0618 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback, CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
#pragma warning restore CS0618 // Type or member is obsolete #pragma warning restore CS0618 // Type or member is obsolete
#if NETCOREAPP3_1
ApplicationProtocols = _tlsParameters.ApplicationProtocols,
#endif
CertificateValidationHandler = _tlsParameters.CertificateValidationHandler, CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors, IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors
@@ -19,7 +19,11 @@ namespace MQTTnet.Client.Options
public Func<MqttClientCertificateValidationCallbackContext, bool> CertificateValidationHandler { get; set; } 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; public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
#endif
#if WINDOWS_UWP #if WINDOWS_UWP
public IEnumerable<IEnumerable<byte>> Certificates { get; set; } public IEnumerable<IEnumerable<byte>> Certificates { get; set; }
@@ -27,7 +31,11 @@ namespace MQTTnet.Client.Options
public IEnumerable<X509Certificate> Certificates { get; set; } public IEnumerable<X509Certificate> Certificates { get; set; }
#endif #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; } public bool IgnoreCertificateChainErrors { get; set; }
@@ -8,7 +8,7 @@ namespace MQTTnet.Client.Options
public int? Port { get; set; } public int? Port { get; set; }
public int BufferSize { get; set; } = 65536; public int BufferSize { get; set; } = 8192;
public bool? DualMode { get; set; } public bool? DualMode { get; set; }
@@ -22,7 +22,15 @@ namespace MQTTnet.Client.Options
public List<X509Certificate> Certificates { get; set; } public List<X509Certificate> Certificates { get; set; }
#endif #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; public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
#endif
[Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")] [Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; } public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; }
@@ -8,10 +8,25 @@ namespace MQTTnet.Client.Publishing
{ {
public ushort? PacketIdentifier { get; set; } 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; 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; } 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; } public List<MqttUserProperty> UserProperties { get; set; }
} }
} }
@@ -5,10 +5,28 @@ namespace MQTTnet.Client.Subscribing
{ {
public class MqttClientSubscribeOptions 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>(); 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; } 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; } public List<MqttUserProperty> UserProperties { get; set; }
} }
} }
@@ -9,6 +9,13 @@ namespace MQTTnet.Client.Subscribing
{ {
private readonly MqttClientSubscribeOptions _subscribeOptions = new MqttClientSubscribeOptions(); 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) public MqttClientSubscribeOptionsBuilder WithUserProperty(string name, string value)
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));
@@ -27,7 +34,6 @@ namespace MQTTnet.Client.Subscribing
public MqttClientSubscribeOptionsBuilder WithSubscriptionIdentifier(uint? subscriptionIdentifier) public MqttClientSubscribeOptionsBuilder WithSubscriptionIdentifier(uint? subscriptionIdentifier)
{ {
_subscribeOptions.SubscriptionIdentifier = subscriptionIdentifier; _subscribeOptions.SubscriptionIdentifier = subscriptionIdentifier;
return this; return this;
} }
@@ -10,8 +10,16 @@ namespace MQTTnet.Client.Subscribing
ResultCode = resultCode; ResultCode = resultCode;
} }
/// <summary>
/// Gets or sets the topic filter.
/// The topic filter can contain topics and wildcards.
/// </summary>
public MqttTopicFilter TopicFilter { get; } public MqttTopicFilter TopicFilter { get; }
/// <summary>
/// Gets or sets the result code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientSubscribeResultCode ResultCode { get; } public MqttClientSubscribeResultCode ResultCode { get; }
} }
} }
@@ -5,8 +5,19 @@ namespace MQTTnet.Client.Unsubscribing
{ {
public class MqttClientUnsubscribeOptions 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>(); 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>(); public List<MqttUserProperty> UserProperties { get; set; } = new List<MqttUserProperty>();
} }
} }
@@ -8,6 +8,13 @@ namespace MQTTnet.Client.Unsubscribing
{ {
private readonly MqttClientUnsubscribeOptions _unsubscribeOptions = new MqttClientUnsubscribeOptions(); 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) public MqttClientUnsubscribeOptionsBuilder WithUserProperty(string name, string value)
{ {
if (name is null) throw new ArgumentNullException(nameof(name)); if (name is null) throw new ArgumentNullException(nameof(name));
@@ -16,6 +23,12 @@ namespace MQTTnet.Client.Unsubscribing
return WithUserProperty(new MqttUserProperty(name, value)); 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) public MqttClientUnsubscribeOptionsBuilder WithUserProperty(MqttUserProperty userProperty)
{ {
if (userProperty is null) throw new ArgumentNullException(nameof(userProperty)); if (userProperty is null) throw new ArgumentNullException(nameof(userProperty));
@@ -10,8 +10,16 @@ namespace MQTTnet.Client.Unsubscribing
ReasonCode = reasonCode; ReasonCode = reasonCode;
} }
/// <summary>
/// Gets or sets the topic filter.
/// The topic filter can contain topics and wildcards.
/// </summary>
public string TopicFilter { get; } public string TopicFilter { get; }
/// <summary>
/// Gets or sets the result code.
/// Hint: MQTT 5 feature only.
/// </summary>
public MqttClientUnsubscribeResultCode ReasonCode { get; } public MqttClientUnsubscribeResultCode ReasonCode { get; }
} }
} }
@@ -4,8 +4,6 @@ namespace MQTTnet.Diagnostics
{ {
public interface IMqttNetLogger public interface IMqttNetLogger
{ {
event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
IMqttNetScopedLogger CreateScopedLogger(string source); IMqttNetScopedLogger CreateScopedLogger(string source);
void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception); void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception);
@@ -2,6 +2,7 @@
namespace MQTTnet.Diagnostics 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 class MqttNetGlobalLogger
{ {
public static event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished; public static event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
@@ -10,8 +11,6 @@ namespace MQTTnet.Diagnostics
public static void Publish(MqttNetLogMessage logMessage) public static void Publish(MqttNetLogMessage logMessage)
{ {
if (logMessage == null) throw new ArgumentNullException(nameof(logMessage));
LogMessagePublished?.Invoke(null, new MqttNetLogMessagePublishedEventArgs(logMessage)); LogMessagePublished?.Invoke(null, new MqttNetLogMessagePublishedEventArgs(logMessage));
} }
} }
@@ -2,7 +2,7 @@
namespace MQTTnet.Diagnostics namespace MQTTnet.Diagnostics
{ {
public class MqttNetLogMessage public sealed class MqttNetLogMessage
{ {
public string LogId { get; set; } public string LogId { get; set; }
@@ -2,18 +2,18 @@
namespace MQTTnet.Diagnostics namespace MQTTnet.Diagnostics
{ {
public class MqttNetLogMessagePublishedEventArgs : EventArgs public sealed class MqttNetLogMessagePublishedEventArgs : EventArgs
{ {
public MqttNetLogMessagePublishedEventArgs(MqttNetLogMessage logMessage) public MqttNetLogMessagePublishedEventArgs(MqttNetLogMessage logMessage)
{ {
LogMessage = logMessage ?? throw new ArgumentNullException(nameof(logMessage)); LogMessage = logMessage ?? throw new ArgumentNullException(nameof(logMessage));
#pragma warning disable CS0618 // Type or member is obsolete #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 #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 TraceMessage { get; }
public MqttNetLogMessage LogMessage { get; } public MqttNetLogMessage LogMessage { get; }
+9 -6
View File
@@ -36,13 +36,16 @@ namespace MQTTnet.Diagnostics
return; return;
} }
try if (parameters?.Length > 0 && message?.Length > 0)
{ {
message = string.Format(message ?? string.Empty, parameters); try
} {
catch (FormatException) message = string.Format(message, parameters);
{ }
message = "MESSAGE FORMAT INVALID: " + message; catch (FormatException)
{
message = "MESSAGE FORMAT INVALID: " + message;
}
} }
var logMessage = new MqttNetLogMessage var logMessage = new MqttNetLogMessage
@@ -18,6 +18,11 @@ namespace MQTTnet.Diagnostics
{ {
logger.Publish(MqttNetLogLevel.Warning, message, parameters, exception); 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) 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"; return "netstandard2.1";
#elif WINDOWS_UWP #elif WINDOWS_UWP
return "uap10.0"; return "uap10.0";
#elif NETCOREAPP3_1
return "netcoreapp3.1";
#elif NET5_0
return "net5.0";
#endif #endif
} }
} }
@@ -13,8 +13,8 @@ namespace MQTTnet.Exceptions
{ {
} }
public MqttCommunicationException(string message) public MqttCommunicationException(string message, Exception innerException = null)
: base(message) : 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.Disconnecting;
using MQTTnet.Client.Options; using MQTTnet.Client.Options;
using MQTTnet.Client.Publishing; using MQTTnet.Client.Publishing;
using MQTTnet.Client.Subscribing; using MQTTnet.Client.Subscribing;
using MQTTnet.Client.Unsubscribing; using MQTTnet.Client.Unsubscribing;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol;
using MQTTnet.Server; using MQTTnet.Server;
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult; using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
@@ -16,6 +18,8 @@ namespace MQTTnet.Formatter
MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket); MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket);
MqttBasePacket CreatePubRecPacket(MqttPublishPacket publishPacket);
MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket); MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket);
MqttClientAuthenticateResult CreateClientConnectResult(MqttConnAckPacket connAckPacket); MqttClientAuthenticateResult CreateClientConnectResult(MqttConnAckPacket connAckPacket);
@@ -30,8 +34,12 @@ namespace MQTTnet.Formatter
MqttSubscribePacket CreateSubscribePacket(MqttClientSubscribeOptions options); MqttSubscribePacket CreateSubscribePacket(MqttClientSubscribeOptions options);
MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, Server.MqttClientSubscribeResult subscribeResult);
MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options); MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options);
MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes);
MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options); MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options);
MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket); MqttClientPublishResult CreatePublishResult(MqttPubAckPacket pubAckPacket);
@@ -1,6 +1,4 @@
using System; namespace MQTTnet.Formatter
namespace MQTTnet.Formatter
{ {
public interface IMqttPacketBodyReader public interface IMqttPacketBodyReader
{ {
@@ -6,13 +6,13 @@ using MQTTnet.Exceptions;
namespace MQTTnet.Formatter namespace MQTTnet.Formatter
{ {
public class MqttPacketBodyReader : IMqttPacketBodyReader public sealed class MqttPacketBodyReader : IMqttPacketBodyReader
{ {
private readonly byte[] _buffer; readonly byte[] _buffer;
private readonly int _initialOffset; readonly int _initialOffset;
private readonly int _length; readonly int _length;
private int _offset; int _offset;
public MqttPacketBodyReader(byte[] buffer, int offset, int length) public MqttPacketBodyReader(byte[] buffer, int offset, int length)
{ {
@@ -8,10 +8,10 @@ using MQTTnet.Packets;
namespace MQTTnet.Formatter namespace MQTTnet.Formatter
{ {
public class MqttPacketFormatterAdapter public sealed class MqttPacketFormatterAdapter
{ {
IMqttPacketFormatter _formatter; IMqttPacketFormatter _formatter;
public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion) public MqttPacketFormatterAdapter(MqttProtocolVersion protocolVersion)
: this(protocolVersion, new MqttPacketWriter()) : this(protocolVersion, new MqttPacketWriter())
{ {
@@ -26,7 +26,7 @@ namespace MQTTnet.Formatter
public MqttPacketFormatterAdapter(IMqttPacketWriter writer) public MqttPacketFormatterAdapter(IMqttPacketWriter writer)
{ {
Writer = writer; Writer = writer;
} }
public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown; public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown;
@@ -39,7 +39,7 @@ namespace MQTTnet.Formatter
return _formatter.DataConverter; return _formatter.DataConverter;
} }
} }
public IMqttPacketWriter Writer { get; } public IMqttPacketWriter Writer { get; }
public ArraySegment<byte> Encode(MqttBasePacket packet) public ArraySegment<byte> Encode(MqttBasePacket packet)
@@ -69,32 +69,21 @@ namespace MQTTnet.Formatter
{ {
var protocolVersion = ParseProtocolVersion(receivedMqttPacket); 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 // the regular CONNECT packet. So it will not properly deserialized if this
// data is missing. // data is missing.
receivedMqttPacket.Body.Seek(0); receivedMqttPacket.BodyReader.Seek(0);
UseProtocolVersion(protocolVersion); 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) public static IMqttPacketFormatter GetMqttPacketFormatter(MqttProtocolVersion protocolVersion, IMqttPacketWriter writer)
{ {
if (protocolVersion == MqttProtocolVersion.Unknown) if (protocolVersion == MqttProtocolVersion.Unknown)
{ {
throw new InvalidOperationException("MQTT protocol version is invalid."); throw new InvalidOperationException("MQTT protocol version is invalid.");
} }
switch (protocolVersion) switch (protocolVersion)
{ {
case MqttProtocolVersion.V500: 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 == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
if (receivedMqttPacket.Body.Length < 7) if (receivedMqttPacket.BodyReader.Length < 7)
{ {
// 2 byte protocol name length // 2 byte protocol name length
// at least 4 byte protocol name // at least 4 byte protocol name
@@ -128,8 +128,8 @@ namespace MQTTnet.Formatter
throw new MqttProtocolViolationException("CONNECT packet must have at least 7 bytes."); throw new MqttProtocolViolationException("CONNECT packet must have at least 7 bytes.");
} }
var protocolName = receivedMqttPacket.Body.ReadStringWithLengthPrefix(); var protocolName = receivedMqttPacket.BodyReader.ReadStringWithLengthPrefix();
var protocolLevel = receivedMqttPacket.Body.ReadByte(); var protocolLevel = receivedMqttPacket.BodyReader.ReadByte();
if (protocolName == "MQTT") 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> /// </summary>
public sealed class MqttPacketWriter : IMqttPacketWriter public sealed class MqttPacketWriter : IMqttPacketWriter
{ {
static readonly ArraySegment<byte> ZeroVariableLengthIntegerArray = new ArraySegment<byte>(new byte[1], 0, 1); public static int InitialBufferSize = 4096;
static readonly ArraySegment<byte> ZeroTwoByteIntegerArray = new ArraySegment<byte>(new byte[2], 0, 2);
public static int InitialBufferSize = 128; public static int MaxBufferSize = 4096 * 4;
public static int MaxBufferSize = 4096;
byte[] _buffer = new byte[InitialBufferSize]; byte[] _buffer = new byte[InitialBufferSize];
@@ -38,59 +36,72 @@ namespace MQTTnet.Formatter
var x = value; var x = value;
do do
{ {
x = x / 128; x /= 128;
result++; result++;
} while (x > 0); } while (x > 0);
return result; return result;
} }
public static ArraySegment<byte> EncodeVariableLengthInteger(uint value) public void WriteVariableLengthInteger(uint value)
{ {
if (value == 0) if (value == 0)
{ {
return ZeroVariableLengthIntegerArray; _buffer[_offset] = 0;
IncreasePosition(1);
return;
} }
if (value <= 127) 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 size = 0;
var bufferOffset = 0;
var x = value; var x = value;
do do
{ {
var encodedByte = x % 128; var encodedByte = x % 128;
x = x / 128; x /= 128;
if (x > 0) if (x > 0)
{ {
encodedByte = encodedByte | 128; encodedByte |= 128;
} }
buffer[bufferOffset] = (byte)encodedByte; _buffer[_offset + size] = (byte)encodedByte;
bufferOffset++; size++;
} while (x > 0); } while (x > 0);
return new ArraySegment<byte>(buffer, 0, bufferOffset); IncreasePosition(size);
}
public void WriteVariableLengthInteger(uint value)
{
Write(EncodeVariableLengthInteger(value));
} }
public void WriteWithLengthPrefix(string value) public void WriteWithLengthPrefix(string value)
{ {
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
{ {
Write(ZeroTwoByteIntegerArray); EnsureAdditionalCapacity(2);
_buffer[_offset] = 0;
_buffer[_offset + 1] = 0;
IncreasePosition(2);
} }
else 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) if (value == null || value.Length == 0)
{ {
Write(ZeroTwoByteIntegerArray); EnsureAdditionalCapacity(2);
_buffer[_offset] = 0;
_buffer[_offset + 1] = 0;
IncreasePosition(2);
} }
else else
{ {
EnsureAdditionalCapacity(value.Length + 2); 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) public void Write(IMqttPacketWriter propertyWriter)
{ {
if (propertyWriter == null) throw new ArgumentNullException(nameof(propertyWriter));
if (propertyWriter is MqttPacketWriter writer) if (propertyWriter is MqttPacketWriter writer)
{ {
if (writer.Length == 0) if (writer.Length == 0)
@@ -156,7 +174,12 @@ namespace MQTTnet.Formatter
return; 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) 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 // 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 // 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 // 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 // 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. // memory for a long time without causing troubles.
if (_buffer.Length < MaxBufferSize) if (_buffer.Length < MaxBufferSize)
@@ -191,11 +214,6 @@ namespace MQTTnet.Formatter
Array.Resize(ref _buffer, MaxBufferSize); Array.Resize(ref _buffer, MaxBufferSize);
} }
void Write(ArraySegment<byte> buffer)
{
Write(buffer.Array, buffer.Offset, buffer.Count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
void EnsureAdditionalCapacity(int additionalCapacity) void EnsureAdditionalCapacity(int additionalCapacity)
{ {
@@ -9,6 +9,7 @@ using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using MQTTnet.Server; using MQTTnet.Server;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult; 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) public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
{ {
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket)); if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
@@ -172,6 +182,18 @@ namespace MQTTnet.Formatter.V3
return subscribePacket; 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) public MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));
@@ -182,6 +204,15 @@ namespace MQTTnet.Formatter.V3
return unsubscribePacket; return unsubscribePacket;
} }
public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes)
{
return new MqttUnsubAckPacket
{
PacketIdentifier = unsubscribePacket.PacketIdentifier,
ReasonCodes = reasonCodes
};
}
public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options) public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
{ {
if (options.ReasonCode != MqttClientDisconnectReason.NormalDisconnection || options.ReasonString != null) if (options.ReasonCode != MqttClientDisconnectReason.NormalDisconnection || options.ReasonString != null)
@@ -63,20 +63,20 @@ namespace MQTTnet.Formatter.V3
switch ((MqttControlPacketType)controlPacketType) switch ((MqttControlPacketType)controlPacketType)
{ {
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body); case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Disconnect: return DisconnectPacket; case MqttControlPacketType.Disconnect: return DisconnectPacket;
case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket); case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket);
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PingReq: return PingReqPacket; case MqttControlPacketType.PingReq: return PingReqPacket;
case MqttControlPacketType.PingResp: return PingRespPacket; case MqttControlPacketType.PingResp: return PingRespPacket;
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body); case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body); case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.BodyReader);
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported."); default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
} }
@@ -202,18 +202,18 @@ namespace MQTTnet.Formatter.V3
static MqttBasePacket DecodePublishPacket(ReceivedMqttPacket receivedMqttPacket) static MqttBasePacket DecodePublishPacket(ReceivedMqttPacket receivedMqttPacket)
{ {
ThrowIfBodyIsEmpty(receivedMqttPacket.Body); ThrowIfBodyIsEmpty(receivedMqttPacket.BodyReader);
var retain = (receivedMqttPacket.FixedHeader & 0x1) > 0; var retain = (receivedMqttPacket.FixedHeader & 0x1) > 0;
var qualityOfServiceLevel = (MqttQualityOfServiceLevel)(receivedMqttPacket.FixedHeader >> 1 & 0x3); var qualityOfServiceLevel = (MqttQualityOfServiceLevel)(receivedMqttPacket.FixedHeader >> 1 & 0x3);
var dup = (receivedMqttPacket.FixedHeader & 0x8) > 0; 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) if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{ {
packetIdentifier = receivedMqttPacket.Body.ReadTwoByteInteger(); packetIdentifier = receivedMqttPacket.BodyReader.ReadTwoByteInteger();
} }
var packet = new MqttPublishPacket var packet = new MqttPublishPacket
@@ -225,9 +225,9 @@ namespace MQTTnet.Formatter.V3
Dup = dup Dup = dup
}; };
if (!receivedMqttPacket.Body.EndOfStream) if (!receivedMqttPacket.BodyReader.EndOfStream)
{ {
packet.Payload = receivedMqttPacket.Body.ReadRemainingData(); packet.Payload = receivedMqttPacket.BodyReader.ReadRemainingData();
} }
return packet; return packet;
@@ -414,12 +414,12 @@ namespace MQTTnet.Formatter.V3
static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter) static byte EncodePubRelPacket(MqttPubRelPacket packet, IMqttPacketWriter packetWriter)
{ {
if (!packet.PacketIdentifier.HasValue) if (packet.PacketIdentifier == 0)
{ {
throw new MqttProtocolViolationException("PubRel packet has no packet identifier."); throw new MqttProtocolViolationException("PubRel packet has no packet identifier.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02); return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
} }
@@ -432,12 +432,12 @@ namespace MQTTnet.Formatter.V3
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{ {
if (!packet.PacketIdentifier.HasValue) if (packet.PacketIdentifier == 0)
{ {
throw new MqttProtocolViolationException("Publish packet has no packet identifier."); throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
} }
else else
{ {
@@ -471,36 +471,36 @@ namespace MQTTnet.Formatter.V3
static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter) static byte EncodePubAckPacket(MqttPubAckPacket packet, IMqttPacketWriter packetWriter)
{ {
if (!packet.PacketIdentifier.HasValue) if (packet.PacketIdentifier == 0)
{ {
throw new MqttProtocolViolationException("PubAck packet has no packet identifier."); throw new MqttProtocolViolationException("PubAck packet has no packet identifier.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck); return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
} }
static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter) static byte EncodePubRecPacket(MqttPubRecPacket packet, IMqttPacketWriter packetWriter)
{ {
if (!packet.PacketIdentifier.HasValue) if (packet.PacketIdentifier == 0)
{ {
throw new MqttProtocolViolationException("PubRec packet has no packet identifier."); throw new MqttProtocolViolationException("PubRec packet has no packet identifier.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec); return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
} }
static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter) static byte EncodePubCompPacket(MqttPubCompPacket packet, IMqttPacketWriter packetWriter)
{ {
if (!packet.PacketIdentifier.HasValue) if (packet.PacketIdentifier == 0)
{ {
throw new MqttProtocolViolationException("PubComp packet has no packet identifier."); throw new MqttProtocolViolationException("PubComp packet has no packet identifier.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp); 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.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."); throw new MqttProtocolViolationException("Subscribe packet has no packet identifier.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
if (packet.TopicFilters?.Count > 0) if (packet.TopicFilters?.Count > 0)
{ {
@@ -530,12 +530,12 @@ namespace MQTTnet.Formatter.V3
static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter) static byte EncodeSubAckPacket(MqttSubAckPacket packet, IMqttPacketWriter packetWriter)
{ {
if (!packet.PacketIdentifier.HasValue) if (packet.PacketIdentifier == 0)
{ {
throw new MqttProtocolViolationException("SubAck packet has no packet identifier."); throw new MqttProtocolViolationException("SubAck packet has no packet identifier.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
if (packet.ReturnCodes?.Any() == true) 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.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."); throw new MqttProtocolViolationException("Unsubscribe packet has no packet identifier.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
if (packet.TopicFilters?.Any() == true) if (packet.TopicFilters?.Any() == true)
{ {
@@ -572,12 +572,12 @@ namespace MQTTnet.Formatter.V3
static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter) static byte EncodeUnsubAckPacket(MqttUnsubAckPacket packet, IMqttPacketWriter packetWriter)
{ {
if (!packet.PacketIdentifier.HasValue) if (packet.PacketIdentifier == 0)
{ {
throw new MqttProtocolViolationException("UnsubAck packet has no packet identifier."); throw new MqttProtocolViolationException("UnsubAck packet has no packet identifier.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck); return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
} }
@@ -4,7 +4,7 @@ using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V3 namespace MQTTnet.Formatter.V3
{ {
public class MqttV311PacketFormatter : MqttV310PacketFormatter public sealed class MqttV311PacketFormatter : MqttV310PacketFormatter
{ {
public MqttV311PacketFormatter(IMqttPacketWriter packetWriter) public MqttV311PacketFormatter(IMqttPacketWriter packetWriter)
: base(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) public MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket)
{ {
return new MqttApplicationMessage return new MqttApplicationMessage
@@ -93,6 +102,7 @@ namespace MQTTnet.Formatter.V5
MaximumPacketSize = connAckPacket.Properties?.MaximumPacketSize, MaximumPacketSize = connAckPacket.Properties?.MaximumPacketSize,
ReasonString = connAckPacket.Properties?.ReasonString, ReasonString = connAckPacket.Properties?.ReasonString,
ReceiveMaximum = connAckPacket.Properties?.ReceiveMaximum, ReceiveMaximum = connAckPacket.Properties?.ReceiveMaximum,
MaximumQoS = connAckPacket.Properties?.MaximumQoS ?? MqttQualityOfServiceLevel.ExactlyOnce,
ResponseInformation = connAckPacket.Properties?.ResponseInformation, ResponseInformation = connAckPacket.Properties?.ResponseInformation,
TopicAliasMaximum = connAckPacket.Properties?.TopicAliasMaximum, TopicAliasMaximum = connAckPacket.Properties?.TopicAliasMaximum,
ServerReference = connAckPacket.Properties?.ServerReference, ServerReference = connAckPacket.Properties?.ServerReference,
@@ -143,7 +153,8 @@ namespace MQTTnet.Formatter.V5
AuthenticationMethod = connectionValidatorContext.AuthenticationMethod, AuthenticationMethod = connectionValidatorContext.AuthenticationMethod,
AuthenticationData = connectionValidatorContext.ResponseAuthenticationData, AuthenticationData = connectionValidatorContext.ResponseAuthenticationData,
AssignedClientIdentifier = connectionValidatorContext.AssignedClientIdentifier, AssignedClientIdentifier = connectionValidatorContext.AssignedClientIdentifier,
ReasonString = connectionValidatorContext.ReasonString ReasonString = connectionValidatorContext.ReasonString,
TopicAliasMaximum = ushort.MaxValue
} }
}; };
} }
@@ -200,6 +211,18 @@ namespace MQTTnet.Formatter.V5
return packet; 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) public MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));
@@ -215,6 +238,15 @@ namespace MQTTnet.Formatter.V5
return packet; return packet;
} }
public MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes)
{
return new MqttUnsubAckPacket
{
PacketIdentifier = unsubscribePacket.PacketIdentifier,
ReasonCodes = reasonCodes
};
}
public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options) public MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options)
{ {
var packet = new MqttDisconnectPacket(); var packet = new MqttDisconnectPacket();
@@ -25,21 +25,21 @@ namespace MQTTnet.Formatter.V5
switch ((MqttControlPacketType)controlPacketType) switch ((MqttControlPacketType)controlPacketType)
{ {
case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.Body); case MqttControlPacketType.Connect: return DecodeConnectPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.ConnAck: return DecodeConnAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Disconnect: return DecodeDisconnectPacket(receivedMqttPacket.Body); case MqttControlPacketType.Disconnect: return DecodeDisconnectPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket.FixedHeader, receivedMqttPacket.Body); case MqttControlPacketType.Publish: return DecodePublishPacket(receivedMqttPacket.FixedHeader, receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubAck: return DecodePubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubRec: return DecodePubRecPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubRel: return DecodePubRelPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.Body); case MqttControlPacketType.PubComp: return DecodePubCompPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.PingReq: return DecodePingReqPacket(); case MqttControlPacketType.PingReq: return DecodePingReqPacket();
case MqttControlPacketType.PingResp: return DecodePingRespPacket(); case MqttControlPacketType.PingResp: return DecodePingRespPacket();
case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.Body); case MqttControlPacketType.Subscribe: return DecodeSubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.SubAck: return DecodeSubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.Body); case MqttControlPacketType.Unsubscibe: return DecodeUnsubscribePacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.Body); case MqttControlPacketType.UnsubAck: return DecodeUnsubAckPacket(receivedMqttPacket.BodyReader);
case MqttControlPacketType.Auth: return DecodeAuthPacket(receivedMqttPacket.Body); case MqttControlPacketType.Auth: return DecodeAuthPacket(receivedMqttPacket.BodyReader);
default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported."); default: throw new MqttProtocolViolationException($"Packet type ({controlPacketType}) not supported.");
} }
@@ -254,6 +254,10 @@ namespace MQTTnet.Formatter.V5
{ {
packet.Properties.ReceiveMaximum = propertiesReader.ReadReceiveMaximum(); packet.Properties.ReceiveMaximum = propertiesReader.ReadReceiveMaximum();
} }
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.MaximumQoS)
{
packet.Properties.MaximumQoS = propertiesReader.ReadMaximumQoS();
}
else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AssignedClientIdentifier) else if (propertiesReader.CurrentPropertyId == MqttPropertyId.AssignedClientIdentifier)
{ {
packet.Properties.AssignedClientIdentifier = propertiesReader.ReadAssignedClientIdentifier(); packet.Properties.AssignedClientIdentifier = propertiesReader.ReadAssignedClientIdentifier();
@@ -6,22 +6,15 @@ using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V5 namespace MQTTnet.Formatter.V5
{ {
public class MqttV500PacketEncoder public sealed class MqttV500PacketEncoder
{ {
private readonly IMqttPacketWriter _packetWriter; readonly IMqttPacketWriter _packetWriter;
public MqttV500PacketEncoder()
: this(new MqttPacketWriter())
{
}
public MqttV500PacketEncoder(IMqttPacketWriter packetWriter) public MqttV500PacketEncoder(IMqttPacketWriter packetWriter)
{ {
_packetWriter = packetWriter; _packetWriter = packetWriter ?? throw new ArgumentNullException(nameof(packetWriter));
} }
public ArraySegment<byte> Encode(MqttBasePacket packet) public ArraySegment<byte> Encode(MqttBasePacket packet)
{ {
if (packet == null) throw new ArgumentNullException(nameof(packet)); if (packet == null) throw new ArgumentNullException(nameof(packet));
@@ -52,7 +45,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.FreeBuffer(); _packetWriter.FreeBuffer();
} }
private static byte EncodePacket(MqttBasePacket packet, IMqttPacketWriter packetWriter) static byte EncodePacket(MqttBasePacket packet, IMqttPacketWriter packetWriter)
{ {
switch (packet) 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 (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
@@ -176,7 +169,7 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect); 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 (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
@@ -203,6 +196,7 @@ namespace MQTTnet.Formatter.V5
propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData); propertiesWriter.WriteAuthenticationData(packet.Properties.AuthenticationData);
propertiesWriter.WriteRetainAvailable(packet.Properties.RetainAvailable); propertiesWriter.WriteRetainAvailable(packet.Properties.RetainAvailable);
propertiesWriter.WriteReceiveMaximum(packet.Properties.ReceiveMaximum); propertiesWriter.WriteReceiveMaximum(packet.Properties.ReceiveMaximum);
propertiesWriter.WriteMaximumQoS(packet.Properties.MaximumQoS);
propertiesWriter.WriteAssignedClientIdentifier(packet.Properties.AssignedClientIdentifier); propertiesWriter.WriteAssignedClientIdentifier(packet.Properties.AssignedClientIdentifier);
propertiesWriter.WriteTopicAliasMaximum(packet.Properties.TopicAliasMaximum); propertiesWriter.WriteTopicAliasMaximum(packet.Properties.TopicAliasMaximum);
propertiesWriter.WriteReasonString(packet.Properties.ReasonString); propertiesWriter.WriteReasonString(packet.Properties.ReasonString);
@@ -221,7 +215,7 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck); 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 (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter));
@@ -235,12 +229,12 @@ namespace MQTTnet.Formatter.V5
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{ {
if (!packet.PacketIdentifier.HasValue) if (packet.PacketIdentifier == 0)
{ {
throw new MqttProtocolViolationException("Publish packet has no packet identifier."); throw new MqttProtocolViolationException("Publish packet has no packet identifier.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
} }
else else
{ {
@@ -253,13 +247,18 @@ namespace MQTTnet.Formatter.V5
var propertiesWriter = new MqttV500PropertiesWriter(); var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null) if (packet.Properties != null)
{ {
propertiesWriter.WritePayloadFormatIndicator(packet.Properties.PayloadFormatIndicator); if (packet.Properties.TopicAlias == 0)
propertiesWriter.WriteMessageExpiryInterval(packet.Properties.MessageExpiryInterval); {
propertiesWriter.WriteTopicAlias(packet.Properties.TopicAlias); 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.WriteResponseTopic(packet.Properties.ResponseTopic); }
propertiesWriter.WriteCorrelationData(packet.Properties.CorrelationData);
propertiesWriter.WriteSubscriptionIdentifiers(packet.Properties.SubscriptionIdentifiers);
propertiesWriter.WriteContentType(packet.Properties.ContentType); 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); propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
} }
@@ -287,12 +286,12 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader); 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 (packet == null) throw new ArgumentNullException(nameof(packet));
if (packetWriter == null) throw new ArgumentNullException(nameof(packetWriter)); 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."); 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."); throw new MqttProtocolViolationException("PubAck packet must contain a reason code.");
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
var propertiesWriter = new MqttV500PropertiesWriter(); var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null) if (packet.Properties != null)
@@ -320,9 +319,9 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck); 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) if (!packet.ReasonCode.HasValue)
{ {
@@ -336,7 +335,7 @@ namespace MQTTnet.Formatter.V5
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRecReasonCode.Success) if (packetWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRecReasonCode.Success)
{ {
@@ -347,9 +346,9 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec); 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) if (!packet.ReasonCode.HasValue)
{ {
@@ -363,7 +362,7 @@ namespace MQTTnet.Formatter.V5
propertiesWriter.WriteUserProperties(packet.Properties.UserProperties); propertiesWriter.WriteUserProperties(packet.Properties.UserProperties);
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
if (propertiesWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRelReasonCode.Success) if (propertiesWriter.Length > 0 || packet.ReasonCode.Value != MqttPubRelReasonCode.Success)
{ {
@@ -374,16 +373,16 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02); 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) if (!packet.ReasonCode.HasValue)
{ {
ThrowReasonCodeNotSetException(); ThrowReasonCodeNotSetException();
} }
packetWriter.Write(packet.PacketIdentifier.Value); packetWriter.Write(packet.PacketIdentifier);
var propertiesWriter = new MqttV500PropertiesWriter(); var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null) if (packet.Properties != null)
@@ -401,13 +400,13 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp); 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]."); 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(); var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null) if (packet.Properties != null)
@@ -448,13 +447,13 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02); 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]."); 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(); var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null) if (packet.Properties != null)
@@ -473,13 +472,13 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck); 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]."); 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(); var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null) if (packet.Properties != null)
@@ -497,13 +496,13 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02); 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]."); 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(); var propertiesWriter = new MqttV500PropertiesWriter();
if (packet.Properties != null) if (packet.Properties != null)
@@ -522,7 +521,7 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck); return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
} }
private static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, IMqttPacketWriter packetWriter) static byte EncodeDisconnectPacket(MqttDisconnectPacket packet, IMqttPacketWriter packetWriter)
{ {
if (!packet.ReasonCode.HasValue) if (!packet.ReasonCode.HasValue)
{ {
@@ -545,17 +544,17 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Disconnect); return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Disconnect);
} }
private static byte EncodePingReqPacket() static byte EncodePingReqPacket()
{ {
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingReq); return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingReq);
} }
private static byte EncodePingRespPacket() static byte EncodePingRespPacket()
{ {
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PingResp); 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); packetWriter.Write((byte)packet.ReasonCode);
@@ -573,14 +572,16 @@ namespace MQTTnet.Formatter.V5
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Auth); return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Auth);
} }
private static void ThrowReasonCodeNotSetException() static void ThrowReasonCodeNotSetException()
{ {
throw new MqttProtocolViolationException("The ReasonCode must be set for MQTT version 5."); 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}."); throw new MqttProtocolViolationException($"Packet identifier is not set for {packet.GetType().Name}.");
} }
@@ -4,15 +4,10 @@ using MQTTnet.Packets;
namespace MQTTnet.Formatter.V5 namespace MQTTnet.Formatter.V5
{ {
public class MqttV500PacketFormatter : IMqttPacketFormatter public sealed class MqttV500PacketFormatter : IMqttPacketFormatter
{ {
private readonly MqttV500PacketEncoder _encoder; readonly MqttV500PacketDecoder _decoder = new MqttV500PacketDecoder();
private readonly MqttV500PacketDecoder _decoder = new MqttV500PacketDecoder(); readonly MqttV500PacketEncoder _encoder;
public MqttV500PacketFormatter()
{
_encoder = new MqttV500PacketEncoder();
}
public MqttV500PacketFormatter(IMqttPacketWriter writer) public MqttV500PacketFormatter(IMqttPacketWriter writer)
{ {
@@ -82,6 +82,17 @@ namespace MQTTnet.Formatter.V5
return _body.ReadTwoByteInteger(); 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() public string ReadAssignedClientIdentifier()
{ {
return _body.ReadStringWithLengthPrefix(); return _body.ReadStringWithLengthPrefix();
@@ -5,10 +5,9 @@ using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V5 namespace MQTTnet.Formatter.V5
{ {
public class MqttV500PropertiesWriter public sealed class MqttV500PropertiesWriter
{ {
// TODO: Consider lazy init on first write to avoid useless allocations. readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
private readonly MqttPacketWriter _packetWriter = new MqttPacketWriter();
public int Length => _packetWriter.Length; public int Length => _packetWriter.Length;
@@ -133,6 +132,17 @@ namespace MQTTnet.Formatter.V5
Write(MqttPropertyId.ReceiveMaximum, value); 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) public void WriteMaximumPacketSize(uint? value)
{ {
WriteAsFourByteInteger(MqttPropertyId.MaximumPacketSize, value); WriteAsFourByteInteger(MqttPropertyId.MaximumPacketSize, value);
@@ -178,7 +188,7 @@ namespace MQTTnet.Formatter.V5
Write(MqttPropertyId.ResponseInformation, value); Write(MqttPropertyId.ResponseInformation, value);
} }
private void Write(MqttPropertyId id, bool? value) void Write(MqttPropertyId id, bool? value)
{ {
if (!value.HasValue) if (!value.HasValue)
{ {
@@ -189,7 +199,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.Write(value.Value ? (byte)0x1 : (byte)0x0); _packetWriter.Write(value.Value ? (byte)0x1 : (byte)0x0);
} }
private void Write(MqttPropertyId id, byte? value) void Write(MqttPropertyId id, byte? value)
{ {
if (!value.HasValue) if (!value.HasValue)
{ {
@@ -200,7 +210,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.Write(value.Value); _packetWriter.Write(value.Value);
} }
private void Write(MqttPropertyId id, ushort? value) void Write(MqttPropertyId id, ushort? value)
{ {
if (!value.HasValue) if (!value.HasValue)
{ {
@@ -211,7 +221,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.Write(value.Value); _packetWriter.Write(value.Value);
} }
private void WriteAsVariableLengthInteger(MqttPropertyId id, uint? value) void WriteAsVariableLengthInteger(MqttPropertyId id, uint? value)
{ {
if (!value.HasValue) if (!value.HasValue)
{ {
@@ -222,7 +232,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.WriteVariableLengthInteger(value.Value); _packetWriter.WriteVariableLengthInteger(value.Value);
} }
private void WriteAsFourByteInteger(MqttPropertyId id, uint? value) void WriteAsFourByteInteger(MqttPropertyId id, uint? value)
{ {
if (!value.HasValue) if (!value.HasValue)
{ {
@@ -236,7 +246,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.Write((byte)value.Value); _packetWriter.Write((byte)value.Value);
} }
private void Write(MqttPropertyId id, string value) void Write(MqttPropertyId id, string value)
{ {
if (value == null) if (value == null)
{ {
@@ -247,7 +257,7 @@ namespace MQTTnet.Formatter.V5
_packetWriter.WriteWithLengthPrefix(value); _packetWriter.WriteWithLengthPrefix(value);
} }
private void Write(MqttPropertyId id, byte[] value) void Write(MqttPropertyId id, byte[] value)
{ {
if (value == null) if (value == null)
{ {
@@ -4,6 +4,10 @@ namespace MQTTnet
{ {
public interface IApplicationMessageReceiver 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; } IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; }
} }
} }
@@ -4,6 +4,7 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Exceptions;
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
@@ -33,6 +34,9 @@ namespace MQTTnet.Implementations
public bool NoDelay 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; get => (int)_socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay) > 0;
set => _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0); set => _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
} }
@@ -48,13 +52,19 @@ namespace MQTTnet.Implementations
get => _socket.ReceiveBufferSize; get => _socket.ReceiveBufferSize;
set => _socket.ReceiveBufferSize = value; set => _socket.ReceiveBufferSize = value;
} }
public int SendBufferSize public int SendBufferSize
{ {
get => _socket.SendBufferSize; get => _socket.SendBufferSize;
set => _socket.SendBufferSize = value; set => _socket.SendBufferSize = value;
} }
public int SendTimeout
{
get => _socket.SendTimeout;
set => _socket.SendTimeout = value;
}
public EndPoint RemoteEndPoint => _socket.RemoteEndPoint; public EndPoint RemoteEndPoint => _socket.RemoteEndPoint;
public bool ReuseAddress public bool ReuseAddress
@@ -69,11 +79,10 @@ namespace MQTTnet.Implementations
{ {
#if NET452 || NET461 #if NET452 || NET461
var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null).ConfigureAwait(false); var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null).ConfigureAwait(false);
return new CrossPlatformSocket(clientSocket);
#else #else
var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false); var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false);
return new CrossPlatformSocket(clientSocket);
#endif #endif
return new CrossPlatformSocket(clientSocket);
} }
catch (ObjectDisposedException) catch (ObjectDisposedException)
{ {
@@ -115,9 +124,19 @@ namespace MQTTnet.Implementations
_networkStream = new NetworkStream(_socket, true); _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) catch (ObjectDisposedException)
{ {
// This will happen when _socket.EndConnect gets called by Task library but the socket is already disposed. // 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.Diagnostics;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using System; using System;
using MQTTnet.Channel;
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
@@ -19,16 +20,19 @@ namespace MQTTnet.Implementations
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null) throw new ArgumentNullException(nameof(options));
IMqttChannel channel;
switch (options.ChannelOptions) switch (options.ChannelOptions)
{ {
case MqttClientTcpOptions _: case MqttClientTcpOptions _:
{ {
return new MqttChannelAdapter(new MqttTcpChannel(options), new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter()), _logger); channel = new MqttTcpChannel(options);
break;
} }
case MqttClientWebSocketOptions webSocketOptions: case MqttClientWebSocketOptions webSocketOptions:
{ {
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttPacketWriter()), _logger); channel = new MqttWebSocketChannel(webSocketOptions);
break;
} }
default: default:
@@ -36,6 +40,9 @@ namespace MQTTnet.Implementations
throw new NotSupportedException(); 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); _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; var socketProtectionLevel = SocketProtectionLevel.Tls12;
if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11) if (_options.TlsOptions.SslProtocol == SslProtocols.Tls11)
{ {
socketProtectionLevel = SocketProtectionLevel.Tls11; socketProtectionLevel = SocketProtectionLevel.Tls11;
} }
#endif
else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls) else if (_options.TlsOptions.SslProtocol == SslProtocols.Tls)
{ {
socketProtectionLevel = SocketProtectionLevel.Tls10; socketProtectionLevel = SocketProtectionLevel.Tls10;
@@ -129,7 +141,7 @@ namespace MQTTnet.Implementations
if (options.TlsOptions.Certificates.Count > 1) 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()); return new Certificate(options.TlsOptions.Certificates.First().AsBuffer());
@@ -10,20 +10,21 @@ using System.Runtime.ExceptionServices;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Exceptions;
namespace MQTTnet.Implementations namespace MQTTnet.Implementations
{ {
public sealed class MqttTcpChannel : IMqttChannel public sealed class MqttTcpChannel : IMqttChannel
{ {
readonly IMqttClientOptions _clientOptions; readonly IMqttClientOptions _clientOptions;
readonly MqttClientTcpOptions _options; readonly MqttClientTcpOptions _tcpOptions;
Stream _stream; Stream _stream;
public MqttTcpChannel(IMqttClientOptions clientOptions) public MqttTcpChannel(IMqttClientOptions clientOptions)
{ {
_clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions)); _clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions));
_options = (MqttClientTcpOptions)clientOptions.ChannelOptions; _tcpOptions = (MqttClientTcpOptions)clientOptions.ChannelOptions;
IsSecureConnection = clientOptions.ChannelOptions?.TlsOptions?.UseTls == true; IsSecureConnection = clientOptions.ChannelOptions?.TlsOptions?.UseTls == true;
} }
@@ -49,43 +50,57 @@ namespace MQTTnet.Implementations
CrossPlatformSocket socket = null; CrossPlatformSocket socket = null;
try try
{ {
if (_options.AddressFamily == AddressFamily.Unspecified) if (_tcpOptions.AddressFamily == AddressFamily.Unspecified)
{ {
socket = new CrossPlatformSocket(); socket = new CrossPlatformSocket();
} }
else else
{ {
socket = new CrossPlatformSocket(_options.AddressFamily); socket = new CrossPlatformSocket(_tcpOptions.AddressFamily);
} }
socket.ReceiveBufferSize = _options.BufferSize; socket.ReceiveBufferSize = _tcpOptions.BufferSize;
socket.SendBufferSize = _options.BufferSize; socket.SendBufferSize = _tcpOptions.BufferSize;
socket.NoDelay = _options.NoDelay; 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 // 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 // because on IPv4 only networks the setter will always throw an exception. Regardless
// of the actual value. // 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(); cancellationToken.ThrowIfCancellationRequested();
var networkStream = socket.GetStream(); var networkStream = socket.GetStream();
if (_options.TlsOptions?.UseTls == true) if (_tcpOptions.TlsOptions?.UseTls == true)
{ {
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback); var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
try 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 catch
{ {
#if NETSTANDARD2_1 #if NETSTANDARD2_1 || NETCOREAPP3_1
await sslStream.DisposeAsync().ConfigureAwait(false); await sslStream.DisposeAsync().ConfigureAwait(false);
#else #else
sslStream.Dispose(); sslStream.Dispose();
@@ -118,22 +133,25 @@ namespace MQTTnet.Implementations
public async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) public async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{ {
if (buffer is null) throw new ArgumentNullException(nameof(buffer));
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
try try
{ {
var stream = _stream;
if (stream == null)
{
return 0;
}
if (!stream.CanRead)
{
return 0;
}
// Workaround for: https://github.com/dotnet/corefx/issues/24430 // Workaround for: https://github.com/dotnet/corefx/issues/24430
using (cancellationToken.Register(Dispose)) 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); 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) public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{ {
if (buffer is null) throw new ArgumentNullException(nameof(buffer));
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
try try
@@ -168,7 +184,7 @@ namespace MQTTnet.Implementations
if (stream == null) 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); await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
@@ -176,6 +192,7 @@ namespace MQTTnet.Implementations
} }
catch (ObjectDisposedException) catch (ObjectDisposedException)
{ {
throw new MqttCommunicationException("The TCP connection is closed.");
} }
catch (IOException exception) catch (IOException exception)
{ {
@@ -209,18 +226,18 @@ namespace MQTTnet.Implementations
bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{ {
#region OBSOLETE #region OBSOLETE
#pragma warning disable CS0618 // Type or member is 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 #pragma warning restore CS0618 // Type or member is obsolete
if (certificateValidationCallback != null) if (certificateValidationCallback != null)
{ {
return certificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions); return certificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions);
} }
#endregion #endregion
var certificateValidationHandler = _options?.TlsOptions?.CertificateValidationHandler; var certificateValidationHandler = _tcpOptions?.TlsOptions?.CertificateValidationHandler;
if (certificateValidationHandler != null) if (certificateValidationHandler != null)
{ {
var context = new MqttClientCertificateValidationCallbackContext var context = new MqttClientCertificateValidationCallbackContext
@@ -228,7 +245,7 @@ namespace MQTTnet.Implementations
Certificate = x509Certificate, Certificate = x509Certificate,
Chain = chain, Chain = chain,
SslPolicyErrors = sslPolicyErrors, SslPolicyErrors = sslPolicyErrors,
ClientOptions = _options ClientOptions = _tcpOptions
}; };
return certificateValidationHandler(context); 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 (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; return false;
} }
@@ -249,24 +266,24 @@ namespace MQTTnet.Implementations
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain)) if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain))
{ {
if (_options?.TlsOptions?.IgnoreCertificateChainErrors != true) if (_tcpOptions?.TlsOptions?.IgnoreCertificateChainErrors != true)
{ {
return false; return false;
} }
} }
return _options?.TlsOptions?.AllowUntrustedCertificates == true; return _tcpOptions?.TlsOptions?.AllowUntrustedCertificates == true;
} }
X509CertificateCollection LoadCertificates() X509CertificateCollection LoadCertificates()
{ {
var certificates = new X509CertificateCollection(); var certificates = new X509CertificateCollection();
if (_options.TlsOptions.Certificates == null) if (_tcpOptions.TlsOptions.Certificates == null)
{ {
return certificates; return certificates;
} }
foreach (var certificate in _options.TlsOptions.Certificates) foreach (var certificate in _tcpOptions.TlsOptions.Certificates)
{ {
certificates.Add(certificate); certificates.Add(certificate);
} }
@@ -48,7 +48,7 @@ namespace MQTTnet.Implementations
if (options.TlsEndpointOptions.IsEnabled) 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); await clientHandler(clientAdapter).ConfigureAwait(false);
} }
@@ -113,7 +113,7 @@ namespace MQTTnet.Implementations
} }
catch (Exception exception) 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."); throw new ArgumentException("TLS certificate is not set.");
} }
var tlsCertificate = options.TlsEndpointOptions.CertificateProvider.GetCertificate(); var tlsCertificate = options.TlsEndpointOptions.CertificateProvider.GetCertificate();
if (!tlsCertificate.HasPrivateKey) if (!tlsCertificate.HasPrivateKey)
{ {
@@ -73,16 +73,22 @@ namespace MQTTnet.Implementations
void Cleanup() void Cleanup()
{ {
_cancellationTokenSource?.Cancel(false); try
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
foreach (var listener in _listeners)
{ {
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) void RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate, CancellationToken cancellationToken)
@@ -59,7 +59,7 @@ namespace MQTTnet.Implementations
_localEndPoint = new IPEndPoint(boundIp, _options.Port); _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); _socket = new CrossPlatformSocket(_addressFamily);
@@ -77,8 +77,8 @@ namespace MQTTnet.Implementations
_socket.Bind(_localEndPoint); _socket.Bind(_localEndPoint);
_socket.Listen(_options.ConnectionBacklog); _socket.Listen(_options.ConnectionBacklog);
Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).Forget(_logger); Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).RunInBackground(_logger);
return true; return true;
} }
@@ -115,7 +115,7 @@ namespace MQTTnet.Implementations
continue; continue;
} }
Task.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken).Forget(_logger); Task.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken).RunInBackground(_logger);
} }
catch (OperationCanceledException) 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); await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
} }
} }
@@ -152,9 +152,7 @@ namespace MQTTnet.Implementations
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6"); _addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
clientSocket.NoDelay = _options.NoDelay; clientSocket.NoDelay = _options.NoDelay;
stream = clientSocket.GetStream(); stream = clientSocket.GetStream();
X509Certificate2 clientCertificate = null; X509Certificate2 clientCertificate = null;
if (_tlsCertificate != null) if (_tlsCertificate != null)
@@ -180,7 +178,11 @@ namespace MQTTnet.Implementations
var clientHandler = ClientHandler; var clientHandler = ClientHandler;
if (clientHandler != null) 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); await clientHandler(clientAdapter).ConfigureAwait(false);
} }
@@ -208,17 +210,17 @@ namespace MQTTnet.Implementations
{ {
stream?.Dispose(); stream?.Dispose();
clientSocket?.Dispose(); clientSocket?.Dispose();
_logger.Verbose("Client '{0}' disconnected at TCP listener '{1}, {2}'.",
remoteEndPoint,
_localEndPoint,
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
} }
catch (Exception disposeException) catch (Exception disposeException)
{ {
_logger.Error(disposeException, "Error while cleaning up client connection"); _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; var certificateValidationHandler = _options.TlsOptions?.CertificateValidationHandler;
#if NETSTANDARD2_1
if (certificateValidationHandler != null) 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. // TODO: Find a way to add client options to same callback. Problem is that they have a different type.
var context = new MqttClientCertificateValidationCallbackContext var context = new MqttClientCertificateValidationCallbackContext
@@ -171,14 +181,9 @@ namespace MQTTnet.Implementations
}; };
return certificateValidationHandler(context); 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 #endif
}
} }
void Cleanup() void Cleanup()
@@ -207,9 +212,9 @@ namespace MQTTnet.Implementations
} }
#if WINDOWS_UWP #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 #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 #else
var proxyUri = new Uri(_options.ProxyOptions.Address); var proxyUri = new Uri(_options.ProxyOptions.Address);

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