From c40e3c64aaf4837dd9854923c229923cafe0946f Mon Sep 17 00:00:00 2001 From: SeppPenner Date: Sun, 29 Mar 2020 17:26:40 +0200 Subject: [PATCH 01/10] Removed checks for user properties in MQTT v3 and instead ignore them. --- .../MQTTnet/Formatter/V3/MqttV310DataConverter.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs b/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs index 48d42cd..4bf4944 100644 --- a/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs +++ b/Source/MQTTnet/Formatter/V3/MqttV310DataConverter.cs @@ -20,11 +20,6 @@ namespace MQTTnet.Formatter.V3 { if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); - if (applicationMessage.UserProperties?.Any() == true) - { - throw new MqttProtocolViolationException("User properties are not supported in MQTT version 3."); - } - return new MqttPublishPacket { Topic = applicationMessage.Topic, @@ -171,11 +166,6 @@ namespace MQTTnet.Formatter.V3 { if (options == null) throw new ArgumentNullException(nameof(options)); - if (options.UserProperties?.Any() == true) - { - throw new MqttProtocolViolationException("User properties are not supported in MQTT version 3."); - } - var subscribePacket = new MqttSubscribePacket(); subscribePacket.TopicFilters.AddRange(options.TopicFilters); @@ -186,11 +176,6 @@ namespace MQTTnet.Formatter.V3 { if (options == null) throw new ArgumentNullException(nameof(options)); - if (options.UserProperties?.Any() == true) - { - throw new MqttProtocolViolationException("User properties are not supported in MQTT version 3."); - } - var unsubscribePacket = new MqttUnsubscribePacket(); unsubscribePacket.TopicFilters.AddRange(options.TopicFilters); From 55dc940abb02786e47fc01da3aa209d640c1860a Mon Sep 17 00:00:00 2001 From: PMExtra Date: Mon, 30 Mar 2020 17:10:04 +0800 Subject: [PATCH 02/10] GetUserProperty extension. --- .../Extensions/UserPropertyExtension.cs | 18 ++++++++++++ .../MqttApplicationMessage_Tests.cs | 29 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 Source/MQTTnet/Extensions/UserPropertyExtension.cs create mode 100644 Tests/MQTTnet.Core.Tests/MqttApplicationMessage_Tests.cs diff --git a/Source/MQTTnet/Extensions/UserPropertyExtension.cs b/Source/MQTTnet/Extensions/UserPropertyExtension.cs new file mode 100644 index 0000000..273be25 --- /dev/null +++ b/Source/MQTTnet/Extensions/UserPropertyExtension.cs @@ -0,0 +1,18 @@ +using System; +using System.Linq; + +namespace MQTTnet.Extensions +{ + public static class UserPropertyExtension + { + public static string GetUserProperty(this MqttApplicationMessage message, string propertyName, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) + { + return message.UserProperties.SingleOrDefault(up => up.Name.Equals(propertyName, comparisonType))?.Value; + } + + public static T GetUserProperty(this MqttApplicationMessage message, string propertyName, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) + { + return (T) Convert.ChangeType(GetUserProperty(message, propertyName, comparisonType), typeof(T)); + } + } +} diff --git a/Tests/MQTTnet.Core.Tests/MqttApplicationMessage_Tests.cs b/Tests/MQTTnet.Core.Tests/MqttApplicationMessage_Tests.cs new file mode 100644 index 0000000..073e4c4 --- /dev/null +++ b/Tests/MQTTnet.Core.Tests/MqttApplicationMessage_Tests.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MQTTnet.Extensions; +using MQTTnet.Packets; + +namespace MQTTnet.Tests +{ + [TestClass] + public class MqttApplicationMessage_Tests + { + [TestMethod] + public void GetUserProperty_Test() + { + var message = new MqttApplicationMessage + { + UserProperties = new List + { + new MqttUserProperty("foo", "bar"), + new MqttUserProperty("value", "1011"), + new MqttUserProperty("CASE", "insensitive") + } + }; + + Assert.AreEqual("bar", message.GetUserProperty("foo")); + Assert.AreEqual(1011, message.GetUserProperty("value")); + Assert.AreEqual("insensitive", message.GetUserProperty("case")); + } + } +} From cb5a0cf38bbf31b314e04aa9470f563366a9f789 Mon Sep 17 00:00:00 2001 From: SeppPenner Date: Mon, 30 Mar 2020 19:38:44 +0200 Subject: [PATCH 03/10] Updated nuget package description. --- Build/MQTTnet.nuspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index a6e7faf..222f260 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -22,8 +22,9 @@ * [Server] Added interceptor for unsubscriptions. * [MQTTnet.Server] Added interceptor for unsubscriptions. * [MQTTnet.AspNetCore] improved compatibility with AspNetCore 3.1 +* [Server] Removed exceptions when user properties are set with MQTT protocol version 3.1 - Copyright Christian Kratky 2016-2019 + Copyright Christian Kratky 2016-2020 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 From f7f00f4e061562c957d398e10130484dca77951d Mon Sep 17 00:00:00 2001 From: PMExtra Date: Tue, 31 Mar 2020 12:03:00 +0800 Subject: [PATCH 04/10] Update nuspec. --- Build/MQTTnet.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index a6e7faf..e09ed01 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -22,6 +22,7 @@ * [Server] Added interceptor for unsubscriptions. * [MQTTnet.Server] Added interceptor for unsubscriptions. * [MQTTnet.AspNetCore] improved compatibility with AspNetCore 3.1 +* [MqttApplicationMessage] Added GetUserProperty convenience method (thanks to @PMExtra). Copyright Christian Kratky 2016-2019 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 From 4eb65fc3d2b865e482ed07e0180bfcb236af97a3 Mon Sep 17 00:00:00 2001 From: PMExtra Date: Tue, 31 Mar 2020 16:42:04 +0800 Subject: [PATCH 05/10] Support MqttClientOptionsBuilder.WithConnectionUri() --- .../MqttClientOptionsBuilderExtension.cs | 48 +++++++++++++++++++ .../MqttClientOptionsBuilder_Tests.cs | 22 +++++++++ 2 files changed, 70 insertions(+) create mode 100644 Source/MQTTnet/Extensions/MqttClientOptionsBuilderExtension.cs create mode 100644 Tests/MQTTnet.Core.Tests/MqttClientOptionsBuilder_Tests.cs diff --git a/Source/MQTTnet/Extensions/MqttClientOptionsBuilderExtension.cs b/Source/MQTTnet/Extensions/MqttClientOptionsBuilderExtension.cs new file mode 100644 index 0000000..fc9da23 --- /dev/null +++ b/Source/MQTTnet/Extensions/MqttClientOptionsBuilderExtension.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq; +using MQTTnet.Client.Options; + +namespace MQTTnet.Extensions +{ + public static class MqttClientOptionsBuilderExtension + { + public static MqttClientOptionsBuilder WithConnectionUri(this MqttClientOptionsBuilder builder, Uri uri) + { + var port = uri.IsDefaultPort ? null : (int?) uri.Port; + switch (uri.Scheme.ToLower()) + { + case "tcp": + case "mqtt": + builder.WithTcpServer(uri.Host, port); + break; + + case "mqtts": + builder.WithTcpServer(uri.Host, port).WithTls(); + break; + + case "ws": + case "wss": + builder.WithWebSocketServer(uri.ToString()); + break; + + default: + throw new ArgumentException("Unexpected scheme in uri."); + } + + if (!string.IsNullOrEmpty(uri.UserInfo)) + { + var userInfo = uri.UserInfo.Split(':'); + var username = userInfo[0]; + var password = userInfo.Length > 1 ? userInfo[1] : ""; + builder.WithCredentials(username, password); + } + + return builder; + } + + public static MqttClientOptionsBuilder WithConnectionUri(this MqttClientOptionsBuilder builder, string uri) + { + return WithConnectionUri(builder, new Uri(uri, UriKind.Absolute)); + } + } +} diff --git a/Tests/MQTTnet.Core.Tests/MqttClientOptionsBuilder_Tests.cs b/Tests/MQTTnet.Core.Tests/MqttClientOptionsBuilder_Tests.cs new file mode 100644 index 0000000..a482081 --- /dev/null +++ b/Tests/MQTTnet.Core.Tests/MqttClientOptionsBuilder_Tests.cs @@ -0,0 +1,22 @@ +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MQTTnet.Client.Options; +using MQTTnet.Extensions; + +namespace MQTTnet.Tests +{ + [TestClass] + public class MqttClientOptionsBuilder_Tests + { + [TestMethod] + public void WithConnectionUri_Credential_Test() + { + var options = new MqttClientOptionsBuilder() + .WithConnectionUri("mqtt://user:password@127.0.0.1") + .Build(); + Assert.AreEqual("user", options.Credentials.Username); + Assert.IsTrue(Encoding.UTF8.GetBytes("password").SequenceEqual(options.Credentials.Password)); + } + } +} From aafd4ea6580a3ed73ea853d19457e1b16cbaa60f Mon Sep 17 00:00:00 2001 From: PMExtra Date: Wed, 1 Apr 2020 15:30:24 +0800 Subject: [PATCH 06/10] Fix. --- Build/MQTTnet.nuspec | 2 +- Source/MQTTnet/Extensions/UserPropertyExtension.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index e09ed01..c409f86 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -22,7 +22,7 @@ * [Server] Added interceptor for unsubscriptions. * [MQTTnet.Server] Added interceptor for unsubscriptions. * [MQTTnet.AspNetCore] improved compatibility with AspNetCore 3.1 -* [MqttApplicationMessage] Added GetUserProperty convenience method (thanks to @PMExtra). +* [Core] Added MqttApplicationMessage.GetUserProperty<T>() convenience method (thanks to @PMExtra). Copyright Christian Kratky 2016-2019 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 diff --git a/Source/MQTTnet/Extensions/UserPropertyExtension.cs b/Source/MQTTnet/Extensions/UserPropertyExtension.cs index 273be25..b4c83b4 100644 --- a/Source/MQTTnet/Extensions/UserPropertyExtension.cs +++ b/Source/MQTTnet/Extensions/UserPropertyExtension.cs @@ -7,7 +7,7 @@ namespace MQTTnet.Extensions { public static string GetUserProperty(this MqttApplicationMessage message, string propertyName, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) { - return message.UserProperties.SingleOrDefault(up => up.Name.Equals(propertyName, comparisonType))?.Value; + return message?.UserProperties?.SingleOrDefault(up => up.Name.Equals(propertyName, comparisonType))?.Value; } public static T GetUserProperty(this MqttApplicationMessage message, string propertyName, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) From 9368eebfd59f123310476406e9d96ed5888deadf Mon Sep 17 00:00:00 2001 From: PMExtra Date: Thu, 2 Apr 2020 16:07:21 +0800 Subject: [PATCH 07/10] Update nuspec. --- Build/MQTTnet.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec index 7e78b6c..5892729 100644 --- a/Build/MQTTnet.nuspec +++ b/Build/MQTTnet.nuspec @@ -22,6 +22,7 @@ * [Server] Added interceptor for unsubscriptions. * [MQTTnet.Server] Added interceptor for unsubscriptions. * [MQTTnet.AspNetCore] improved compatibility with AspNetCore 3.1 +* [Client] Support WithConnectionUri to configure client (thanks to @PMExtra). Copyright Christian Kratky 2016-2020 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 From 763d9778bb66bf131960d9281d243d9a826219a5 Mon Sep 17 00:00:00 2001 From: PMExtra Date: Thu, 2 Apr 2020 17:29:00 +0800 Subject: [PATCH 08/10] Check parameters and add friendly exceptions. --- .../MQTTnet/Extensions/UserPropertyExtension.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Source/MQTTnet/Extensions/UserPropertyExtension.cs b/Source/MQTTnet/Extensions/UserPropertyExtension.cs index b4c83b4..8918600 100644 --- a/Source/MQTTnet/Extensions/UserPropertyExtension.cs +++ b/Source/MQTTnet/Extensions/UserPropertyExtension.cs @@ -7,12 +7,24 @@ namespace MQTTnet.Extensions { public static string GetUserProperty(this MqttApplicationMessage message, string propertyName, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) { - return message?.UserProperties?.SingleOrDefault(up => up.Name.Equals(propertyName, comparisonType))?.Value; + if (message == null) throw new ArgumentNullException(nameof(message)); + if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); + + return message.UserProperties?.SingleOrDefault(up => up.Name.Equals(propertyName, comparisonType))?.Value; } public static T GetUserProperty(this MqttApplicationMessage message, string propertyName, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) { - return (T) Convert.ChangeType(GetUserProperty(message, propertyName, comparisonType), typeof(T)); + var value = GetUserProperty(message, propertyName, comparisonType); + + try + { + return (T) Convert.ChangeType(value, typeof(T)); + } + catch (Exception ex) + { + throw new InvalidOperationException($"Cannot convert value({value}) of UserProperty({propertyName}) to {typeof(T).FullName}.", ex); + } } } } From c83bec6e81421553741d39d4966b9d3ee8d09e22 Mon Sep 17 00:00:00 2001 From: PMExtra Date: Fri, 3 Apr 2020 14:44:04 +0800 Subject: [PATCH 09/10] Fix for Nullable. --- Source/MQTTnet/Extensions/UserPropertyExtension.cs | 4 +++- Source/MQTTnet/MQTTnet.csproj | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/MQTTnet/Extensions/UserPropertyExtension.cs b/Source/MQTTnet/Extensions/UserPropertyExtension.cs index 8918600..e950141 100644 --- a/Source/MQTTnet/Extensions/UserPropertyExtension.cs +++ b/Source/MQTTnet/Extensions/UserPropertyExtension.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Linq; namespace MQTTnet.Extensions @@ -17,9 +18,10 @@ namespace MQTTnet.Extensions { var value = GetUserProperty(message, propertyName, comparisonType); + var typeDescriptor = TypeDescriptor.GetConverter(typeof(T)); try { - return (T) Convert.ChangeType(value, typeof(T)); + return (T) typeDescriptor.ConvertFromString(value); } catch (Exception ex) { diff --git a/Source/MQTTnet/MQTTnet.csproj b/Source/MQTTnet/MQTTnet.csproj index 28e3f3d..07b9161 100644 --- a/Source/MQTTnet/MQTTnet.csproj +++ b/Source/MQTTnet/MQTTnet.csproj @@ -42,6 +42,7 @@ + From 774d49a908a62b893f67f9b6a07894bfd01cd386 Mon Sep 17 00:00:00 2001 From: PMExtra Date: Fri, 3 Apr 2020 14:49:39 +0800 Subject: [PATCH 10/10] Fix exception message and add test cases. --- Source/MQTTnet/Extensions/UserPropertyExtension.cs | 2 +- Tests/MQTTnet.Core.Tests/MqttApplicationMessage_Tests.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/MQTTnet/Extensions/UserPropertyExtension.cs b/Source/MQTTnet/Extensions/UserPropertyExtension.cs index e950141..5bb28cf 100644 --- a/Source/MQTTnet/Extensions/UserPropertyExtension.cs +++ b/Source/MQTTnet/Extensions/UserPropertyExtension.cs @@ -25,7 +25,7 @@ namespace MQTTnet.Extensions } catch (Exception ex) { - throw new InvalidOperationException($"Cannot convert value({value}) of UserProperty({propertyName}) to {typeof(T).FullName}.", ex); + throw new InvalidOperationException($"Cannot convert value({value ?? "null"}) of UserProperty({propertyName}) to {typeof(T).FullName}.", ex); } } } diff --git a/Tests/MQTTnet.Core.Tests/MqttApplicationMessage_Tests.cs b/Tests/MQTTnet.Core.Tests/MqttApplicationMessage_Tests.cs index 073e4c4..6b791dc 100644 --- a/Tests/MQTTnet.Core.Tests/MqttApplicationMessage_Tests.cs +++ b/Tests/MQTTnet.Core.Tests/MqttApplicationMessage_Tests.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using MQTTnet.Extensions; @@ -24,6 +25,9 @@ namespace MQTTnet.Tests Assert.AreEqual("bar", message.GetUserProperty("foo")); Assert.AreEqual(1011, message.GetUserProperty("value")); Assert.AreEqual("insensitive", message.GetUserProperty("case")); + Assert.AreEqual(null, message.GetUserProperty("nonExists")); + Assert.AreEqual(null, message.GetUserProperty("nonExists")); + Assert.ThrowsException(() => message.GetUserProperty("nonExists")); } } }