Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,21 @@ jobs:
uses: actions/checkout@v4

- name: Load strong name certificate
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
run: |
echo "$SNC_BASE64" | base64 --decode > "${{ github.workspace }}/certificate.snk"
shell: bash
env:
SNC_BASE64: ${{ secrets.SNC_BASE64 }}

- name: Build package
- name: Build package (signed)
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
run: dotnet build MQTTnet.sln --configuration Release /p:FileVersion=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }} /p:PackageVersion=${{ env.VERSION }}${{ env.PACKAGE_SUFFIX }} /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=${{ github.workspace }}/certificate.snk

- name: Build package (unsigned)
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }}
run: dotnet build MQTTnet.sln --configuration Release /p:FileVersion=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }} /p:PackageVersion=${{ env.VERSION }}${{ env.PACKAGE_SUFFIX }}

- name: Upload nuget packages
uses: actions/upload-artifact@v4
with:
Expand All @@ -55,12 +61,12 @@ jobs:
uses: actions/checkout@v4

- name: Execute tests
run: dotnet test --framework net10.0 --configuration Release Source/MQTTnet.Tests/MQTTnet.Tests.csproj
run: dotnet test --framework net10.0 --configuration Release --project Source/MQTTnet.Tests/MQTTnet.Tests.csproj

sign:
needs: build
runs-on: windows-latest # Code signing must run on a Windows agent for Authenticode signing (dll/exe)
if: github.repository == 'dotnet/MQTTnet'
if: github.repository == 'dotnet/MQTTnet' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
steps:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
Expand Down
20 changes: 20 additions & 0 deletions Samples/Client/Client_Publish_Samples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
// ReSharper disable UnusedMember.Global
// ReSharper disable InconsistentNaming

using System;
using System.Text;

namespace MQTTnet.Samples.Client;

public static class Client_Publish_Samples
Expand Down Expand Up @@ -84,4 +87,21 @@ public static async Task Publish_Multiple_Application_Messages()

Console.WriteLine("MQTT application message is published.");
}

public static MqttApplicationMessage Create_Message_With_Binary_User_Property()
{
/*
* MQTT v5 user properties are encoded as UTF-8 strings. When the UTF-8 payload is already available
* as a byte buffer, the builder APIs can avoid creating intermediate strings.
*/

var encodedValue = Encoding.UTF8.GetBytes("sensor-01");

return new MqttApplicationMessageBuilder()
.WithTopic("samples/metadata/binary")
.WithUserProperty("client-id", encodedValue.AsMemory())
.WithUserProperty("checksum", new ArraySegment<byte>(encodedValue))
.WithPayload("metadata")
.Build();
}
}
11 changes: 8 additions & 3 deletions Source/MQTTnet.Tests/MQTTnet.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
<LangVersion>default</LangVersion>
<Nullable>disable</Nullable>
<NoWarn>$(NoWarn);CA1707</NoWarn>
<EnableMSTestRunner>true</EnableMSTestRunner>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="MSTest" Version="4.0.1" />

<PackageReference Include="MSTest" Version="4.0.2" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

Expand All @@ -24,4 +23,10 @@
<ProjectReference Include="..\MQTTnet.Extensions.TopicTemplate\MQTTnet.Extensions.TopicTemplate.csproj" />
</ItemGroup>


<ItemGroup>
<None Include=".\..\..\global.json"
Link="global.json"
CopyToOutputDirectory="PreserveNewest"/>
</ItemGroup>
</Project>
17 changes: 9 additions & 8 deletions Source/MQTTnet.Tests/MQTTv5/Client_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Microsoft.IO;
using MQTTnet.Formatter;
using MQTTnet.Internal;
Expand Down Expand Up @@ -64,8 +65,8 @@ public async Task Connect_With_New_Mqtt_Features()
await client.PublishAsync(
new MqttApplicationMessageBuilder().WithTopic("a")
.WithPayload("x")
.WithUserProperty("a", "1")
.WithUserProperty("b", "2")
.WithUserProperty("a", Encoding.UTF8.GetBytes("1"))
.WithUserProperty("b", Encoding.UTF8.GetBytes("2"))
.WithPayloadFormatIndicator(MqttPayloadFormatIndicator.CharacterData)
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.Build());
Expand Down Expand Up @@ -98,8 +99,8 @@ public async Task Publish_And_Receive_New_Properties()
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("Hello")
.WithPayload("World")
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
.WithUserProperty("x", "1")
.WithUserProperty("y", "2")
.WithUserProperty("x", Encoding.UTF8.GetBytes("1"))
.WithUserProperty("y", Encoding.UTF8.GetBytes("2"))
.WithResponseTopic("response")
.WithContentType("text")
.WithMessageExpiryInterval(50)
Expand Down Expand Up @@ -214,8 +215,8 @@ public async Task Publish_With_Properties()
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("Hello")
.WithPayload("World")
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
.WithUserProperty("x", "1")
.WithUserProperty("y", "2")
.WithUserProperty("x", Encoding.UTF8.GetBytes("1"))
.WithUserProperty("y", Encoding.UTF8.GetBytes("2"))
.WithResponseTopic("response")
.WithContentType("text")
.WithMessageExpiryInterval(50)
Expand Down Expand Up @@ -250,8 +251,8 @@ public async Task Publish_With_RecyclableMemoryStream()
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("Hello")
.WithPayload(memoryStream.GetReadOnlySequence())
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
.WithUserProperty("x", "1")
.WithUserProperty("y", "2")
.WithUserProperty("x", Encoding.UTF8.GetBytes("1"))
.WithUserProperty("y", Encoding.UTF8.GetBytes("2"))
.WithResponseTopic("response")
.WithContentType("text")
.WithMessageExpiryInterval(50)
Expand Down
63 changes: 63 additions & 0 deletions Source/MQTTnet.Tests/MqttApplicationMessageBuilder_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Text;
using MQTTnet.Formatter;
using MQTTnet.Formatter.V5;
using MQTTnet.Packets;
using MQTTnet.Protocol;

namespace MQTTnet.Tests;
Expand Down Expand Up @@ -59,4 +64,62 @@ public void CreateApplicationMessage_QosLevel2()
Assert.IsTrue(message.Retain);
Assert.AreEqual(MqttQualityOfServiceLevel.ExactlyOnce, message.QualityOfServiceLevel);
}

[TestMethod]
public void CreateApplicationMessage_UserProperty_ReadOnlyMemoryValue()
{
var value = "utf8";
var buffer = Encoding.UTF8.GetBytes(value);

var message = new MqttApplicationMessageBuilder().WithTopic("topic").WithUserProperty("name", buffer.AsMemory()).Build();

Assert.IsNotNull(message.UserProperties);
Assert.HasCount(1, message.UserProperties);

var userProperty = message.UserProperties[0];
CollectionAssert.AreEqual(buffer, userProperty.ValueBuffer.ToArray());
Assert.AreEqual(value, userProperty.Value);
}

[TestMethod]
public void CreateApplicationMessage_UserProperty_ArraySegmentValue()
{
var buffer = Encoding.UTF8.GetBytes("segment");
var segment = new ArraySegment<byte>(buffer);

var message = new MqttApplicationMessageBuilder().WithTopic("topic").WithUserProperty("name", segment).Build();

Assert.IsNotNull(message.UserProperties);
Assert.HasCount(1, message.UserProperties);

var userProperty = message.UserProperties[0];
CollectionAssert.AreEqual(buffer, userProperty.ValueBuffer.ToArray());
Assert.AreEqual("segment", userProperty.Value);
}

[TestMethod]
public void WriteUserProperty_FromBinaryBuffer_EqualsStringEncoding()
{
var name = "name";
var value = "value";
var encoded = Encoding.UTF8.GetBytes(value);

var binaryWriter = new MqttBufferWriter(32, 256);
var binaryPropertiesWriter = new MqttV5PropertiesWriter(binaryWriter);
binaryPropertiesWriter.WriteUserProperties(new List<MqttUserProperty> { new(name, encoded.AsMemory()) });

var stringWriter = new MqttBufferWriter(32, 256);
var stringPropertiesWriter = new MqttV5PropertiesWriter(stringWriter);
stringPropertiesWriter.WriteUserProperties(new List<MqttUserProperty> { new(name, value) });

CollectionAssert.AreEqual(GetWrittenBytes(stringWriter), GetWrittenBytes(binaryWriter));
}

static byte[] GetWrittenBytes(MqttBufferWriter writer)
{
var length = writer.Length;
var copy = new byte[length];
Array.Copy(writer.GetBuffer(), copy, length);
return copy;
}
}
5 changes: 3 additions & 2 deletions Source/MQTTnet.Tests/MqttApplicationMessageValidator_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Text;
using MQTTnet.Formatter;

namespace MQTTnet.Tests;
Expand All @@ -27,15 +28,15 @@ public void Succeed_When_Using_TopicAlias_And_MQTT_500()
public void Succeed_When_Using_UserProperties_And_MQTT_500()
{
MqttApplicationMessageValidator.ThrowIfNotSupported(
new MqttApplicationMessageBuilder().WithTopic("A").WithUserProperty("User", "Property").Build(),
new MqttApplicationMessageBuilder().WithTopic("A").WithUserProperty("User", Encoding.UTF8.GetBytes("Property")).Build(),
MqttProtocolVersion.V500);
}

[TestMethod]
public void Succeed_When_Using_WillUserProperties_And_MQTT_311()
{
Assert.ThrowsExactly<NotSupportedException>(() => MqttApplicationMessageValidator.ThrowIfNotSupported(
new MqttApplicationMessageBuilder().WithTopic("B").WithUserProperty("User", "Property").Build(),
new MqttApplicationMessageBuilder().WithTopic("B").WithUserProperty("User", Encoding.UTF8.GetBytes("Property")).Build(),
MqttProtocolVersion.V311));
}
}
5 changes: 3 additions & 2 deletions Source/MQTTnet.Tests/MqttTcpChannel_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ public async Task Dispose_Channel_While_Used()

try
{
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 50001));
serverSocket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
var serverPort = ((IPEndPoint)serverSocket.LocalEndPoint).Port;
serverSocket.Listen(0);

#pragma warning disable 4014
Expand All @@ -37,7 +38,7 @@ public async Task Dispose_Channel_While_Used()
},
ct.Token);

var remoteEndPoint = new DnsEndPoint("localhost", 50001);
var remoteEndPoint = new DnsEndPoint("localhost", serverPort);
using var clientSocket = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
await clientSocket.ConnectAsync(remoteEndPoint, CancellationToken.None);

Expand Down
2 changes: 1 addition & 1 deletion Source/MQTTnet.Tests/Server/Cross_Version_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public async Task Send_V500_Receive_V311()

var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("My/Message")
.WithPayload("My_Payload")
.WithUserProperty("A", "B")
.WithUserProperty("A", Encoding.UTF8.GetBytes("B"))
.WithResponseTopic("Response")
.WithCorrelationData(Encoding.UTF8.GetBytes("Correlation"))
.Build();
Expand Down
Loading
Loading