@@ -22,6 +22,9 @@ | |||
* [Server] Added interceptor for unsubscriptions. | |||
* [MQTTnet.Server] Added interceptor for unsubscriptions. | |||
* [MQTTnet.AspNetCore] improved compatibility with AspNetCore 3.1 | |||
* [Core] Added MqttApplicationMessage.GetUserProperty() convenience method (thanks to @PMExtra). | |||
* [Client] Support WithConnectionUri to configure client (thanks to @PMExtra). | |||
* [Server] Removed exceptions when user properties are set with MQTT protocol version 3.1 | |||
* [LowLevelMqttClient] Added low level MQTT client in order to provide more flexibility when using the MQTT protocol. This client requires detailed knowledge about the MQTT protocol. | |||
</releaseNotes> | |||
<copyright>Copyright Christian Kratky 2016-2020</copyright> | |||
@@ -0,0 +1,48 @@ | |||
using System; | |||
using System.Linq; | |||
using MQTTnet.Client.Options; | |||
namespace MQTTnet.Extensions | |||
{ | |||
public static class MqttClientOptionsBuilderExtension | |||
{ | |||
public static MqttClientOptionsBuilder WithConnectionUri(this MqttClientOptionsBuilder builder, Uri uri) | |||
{ | |||
var port = uri.IsDefaultPort ? null : (int?) uri.Port; | |||
switch (uri.Scheme.ToLower()) | |||
{ | |||
case "tcp": | |||
case "mqtt": | |||
builder.WithTcpServer(uri.Host, port); | |||
break; | |||
case "mqtts": | |||
builder.WithTcpServer(uri.Host, port).WithTls(); | |||
break; | |||
case "ws": | |||
case "wss": | |||
builder.WithWebSocketServer(uri.ToString()); | |||
break; | |||
default: | |||
throw new ArgumentException("Unexpected scheme in uri."); | |||
} | |||
if (!string.IsNullOrEmpty(uri.UserInfo)) | |||
{ | |||
var userInfo = uri.UserInfo.Split(':'); | |||
var username = userInfo[0]; | |||
var password = userInfo.Length > 1 ? userInfo[1] : ""; | |||
builder.WithCredentials(username, password); | |||
} | |||
return builder; | |||
} | |||
public static MqttClientOptionsBuilder WithConnectionUri(this MqttClientOptionsBuilder builder, string uri) | |||
{ | |||
return WithConnectionUri(builder, new Uri(uri, UriKind.Absolute)); | |||
} | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
using System; | |||
using System.ComponentModel; | |||
using System.Linq; | |||
namespace MQTTnet.Extensions | |||
{ | |||
public static class UserPropertyExtension | |||
{ | |||
public static string GetUserProperty(this MqttApplicationMessage message, string propertyName, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) | |||
{ | |||
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<T>(this MqttApplicationMessage message, string propertyName, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) | |||
{ | |||
var value = GetUserProperty(message, propertyName, comparisonType); | |||
var typeDescriptor = TypeDescriptor.GetConverter(typeof(T)); | |||
try | |||
{ | |||
return (T) typeDescriptor.ConvertFromString(value); | |||
} | |||
catch (Exception ex) | |||
{ | |||
throw new InvalidOperationException($"Cannot convert value({value ?? "null"}) of UserProperty({propertyName}) to {typeof(T).FullName}.", ex); | |||
} | |||
} | |||
} | |||
} |
@@ -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); | |||
@@ -42,6 +42,7 @@ | |||
</PropertyGroup> | |||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.3'"> | |||
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" /> | |||
<PackageReference Include="System.Net.Security" Version="4.3.2" /> | |||
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" /> | |||
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.2" /> | |||
@@ -0,0 +1,33 @@ | |||
using System; | |||
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<MqttUserProperty> | |||
{ | |||
new MqttUserProperty("foo", "bar"), | |||
new MqttUserProperty("value", "1011"), | |||
new MqttUserProperty("CASE", "insensitive") | |||
} | |||
}; | |||
Assert.AreEqual("bar", message.GetUserProperty("foo")); | |||
Assert.AreEqual(1011, message.GetUserProperty<int>("value")); | |||
Assert.AreEqual("insensitive", message.GetUserProperty("case")); | |||
Assert.AreEqual(null, message.GetUserProperty("nonExists")); | |||
Assert.AreEqual(null, message.GetUserProperty<int?>("nonExists")); | |||
Assert.ThrowsException<InvalidOperationException>(() => message.GetUserProperty<int>("nonExists")); | |||
} | |||
} | |||
} |
@@ -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)); | |||
} | |||
} | |||
} |