Add PayloadSegmentable feature (#1585)

This commit is contained in:
老九
2023-02-22 20:53:55 +01:00
committed by GitHub
parent ba57537108
commit d0584413ca
24 changed files with 260 additions and 122 deletions
+1
View File
@@ -1,4 +1,5 @@
* [Core] Added net7.0 builds (#1635, thanks to @YAJeff). * [Core] Added net7.0 builds (#1635, thanks to @YAJeff).
* [Core] Added support for payload segments (#1585, thanks to @xljiulang)
* [Client] Added support for passing MQTT v5 options (User properties etc.) for disconnects. * [Client] Added support for passing MQTT v5 options (User properties etc.) for disconnects.
* [Client] An internal exception is no longer caught silently when calling _DisconnectAsync_ to indicate that the disconnect is not clean (BREAKING CHANGE). * [Client] An internal exception is no longer caught silently when calling _DisconnectAsync_ to indicate that the disconnect is not clean (BREAKING CHANGE).
* [Client] MQTTv5 features are now checked and an exception is thrown if they are used when using protocol version 3.1.1 and lower. These checks can be disabled in client options. (BREAKING CHANGE!). * [Client] MQTTv5 features are now checked and an exception is thrown if they are used when using protocol version 3.1.1 and lower. These checks can be disabled in client options. (BREAKING CHANGE!).
@@ -103,7 +103,7 @@ namespace MQTTnet.AspNetCore.Tests
connection.Transport = pipe; connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection); var ctx = new MqttConnectionContext(serializer, connection);
await ctx.SendPacketAsync(new MqttPublishPacket { Payload = new byte[20_000] }, CancellationToken.None).ConfigureAwait(false); await ctx.SendPacketAsync(new MqttPublishPacket { PayloadSegment = new ArraySegment<byte>(new byte[20_000]) }, CancellationToken.None).ConfigureAwait(false);
var readResult = await pipe.Send.Reader.ReadAsync(); var readResult = await pipe.Send.Reader.ReadAsync();
Assert.IsTrue(readResult.Buffer.Length > 20000); Assert.IsTrue(readResult.Buffer.Length > 20000);
@@ -19,7 +19,7 @@ namespace MQTTnet.AspNetCore.Tests
{ {
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535)); var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
var buffer = serializer.Encode(new MqttPublishPacket {Topic = "a", Payload = new byte[5]}).Join(); var buffer = serializer.Encode(new MqttPublishPacket {Topic = "a", PayloadSegment = new byte[5]}).Join();
var sequence = new ReadOnlySequence<byte>(buffer.Array, buffer.Offset, buffer.Count); var sequence = new ReadOnlySequence<byte>(buffer.Array, buffer.Offset, buffer.Count);
@@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Client; using MQTTnet.Client;
@@ -119,7 +120,6 @@ namespace MQTTnet.Extensions.Rpc
finally finally
{ {
_waitingCalls.TryRemove(responseTopic, out _); _waitingCalls.TryRemove(responseTopic, out _);
await _mqttClient.UnsubscribeAsync(responseTopic, CancellationToken.None).ConfigureAwait(false); await _mqttClient.UnsubscribeAsync(responseTopic, CancellationToken.None).ConfigureAwait(false);
} }
} }
@@ -131,7 +131,8 @@ namespace MQTTnet.Extensions.Rpc
return CompletedTask.Instance; return CompletedTask.Instance;
} }
awaitable.TrySetResult(eventArgs.ApplicationMessage.Payload); var payloadBuffer = eventArgs.ApplicationMessage.PayloadSegment.ToArray();
awaitable.TrySetResult(payloadBuffer);
// Set this message to handled to that other code can avoid execution etc. // Set this message to handled to that other code can avoid execution etc.
eventArgs.IsHandled = true; eventArgs.IsHandled = true;
+10 -1
View File
@@ -33,9 +33,18 @@ namespace MQTTnet.TestApp
client.ApplicationMessageReceivedAsync += e => client.ApplicationMessageReceivedAsync += e =>
{ {
var payloadText = string.Empty;
if (e.ApplicationMessage.PayloadSegment.Count > 0)
{
payloadText = Encoding.UTF8.GetString(
e.ApplicationMessage.PayloadSegment.Array,
e.ApplicationMessage.PayloadSegment.Offset,
e.ApplicationMessage.PayloadSegment.Count);
}
Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###"); Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}"); Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}"); Console.WriteLine($"+ Payload = {payloadText}");
Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}"); Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}"); Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
Console.WriteLine(); Console.WriteLine();
+4 -4
View File
@@ -196,7 +196,7 @@ namespace MQTTnet.TestApp
return new MqttApplicationMessage return new MqttApplicationMessage
{ {
Topic = "A/B/C", Topic = "A/B/C",
Payload = Encoding.UTF8.GetBytes(Payload), PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(Payload)),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce
}; };
} }
@@ -229,7 +229,7 @@ namespace MQTTnet.TestApp
var message = new MqttApplicationMessage var message = new MqttApplicationMessage
{ {
Topic = "A/B/C", Topic = "A/B/C",
Payload = Encoding.UTF8.GetBytes("Hello World"), PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce
}; };
@@ -280,7 +280,7 @@ namespace MQTTnet.TestApp
var message = new MqttApplicationMessage var message = new MqttApplicationMessage
{ {
Topic = "A/B/C", Topic = "A/B/C",
Payload = Encoding.UTF8.GetBytes("Hello World"), PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce
}; };
@@ -331,7 +331,7 @@ namespace MQTTnet.TestApp
var message = new MqttApplicationMessage var message = new MqttApplicationMessage
{ {
Topic = "A/B/C", Topic = "A/B/C",
Payload = Encoding.UTF8.GetBytes("Hello World"), PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
}; };
+11 -4
View File
@@ -5,6 +5,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Diagnostics; using MQTTnet.Diagnostics;
@@ -100,7 +101,7 @@ namespace MQTTnet.TestApp
{ {
// Replace the payload with the timestamp. But also extending a JSON // Replace the payload with the timestamp. But also extending a JSON
// based payload with the timestamp is a suitable use case. // based payload with the timestamp is a suitable use case.
e.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(DateTime.Now.ToString("O")); e.ApplicationMessage.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(DateTime.Now.ToString("O")));
} }
if (e.ApplicationMessage.Topic == "not_allowed_topic") if (e.ApplicationMessage.Topic == "not_allowed_topic")
@@ -143,10 +144,16 @@ namespace MQTTnet.TestApp
mqttServer.InterceptingPublishAsync += e => mqttServer.InterceptingPublishAsync += e =>
{ {
MqttNetConsoleLogger.PrintToConsole( var payloadText = string.Empty;
$"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'", if (e.ApplicationMessage.PayloadSegment.Count > 0)
ConsoleColor.Magenta); {
payloadText = Encoding.UTF8.GetString(
e.ApplicationMessage.PayloadSegment.Array,
e.ApplicationMessage.PayloadSegment.Offset,
e.ApplicationMessage.PayloadSegment.Count);
}
MqttNetConsoleLogger.PrintToConsole($"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{payloadText}'", ConsoleColor.Magenta);
return CompletedTask.Instance; return CompletedTask.Instance;
}; };
@@ -151,6 +151,8 @@ namespace MQTTnet.Tests.Clients.MqttClient
// Perform a clean disconnect. // Perform a clean disconnect.
await client.DisconnectAsync(disconnectOptions); await client.DisconnectAsync(disconnectOptions);
await LongTestDelay();
Assert.IsNotNull(eventArgs); Assert.IsNotNull(eventArgs);
Assert.IsNotNull(eventArgs.UserProperties); Assert.IsNotNull(eventArgs.UserProperties);
Assert.AreEqual(1, eventArgs.UserProperties.Count); Assert.AreEqual(1, eventArgs.UserProperties.Count);
@@ -111,7 +111,7 @@ namespace MQTTnet.Tests.Formatter
PacketIdentifier = 123, PacketIdentifier = 123,
Dup = true, Dup = true,
Retain = true, Retain = true,
Payload = Encoding.ASCII.GetBytes("HELLO"), PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("HELLO")),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
Topic = "A/B/C" Topic = "A/B/C"
}; };
@@ -318,10 +318,9 @@ namespace MQTTnet.Tests.Formatter
var publishPacket = new MqttPublishPacket var publishPacket = new MqttPublishPacket
{ {
Topic = "abcdefghijklmnopqrstuvwxyz0123456789", Topic = "abcdefghijklmnopqrstuvwxyz0123456789",
Payload = payload PayloadSegment = new ArraySegment<byte>(payload)
}; };
var serializationHelper = new MqttPacketSerializationHelper(); var serializationHelper = new MqttPacketSerializationHelper();
var buffer = serializationHelper.Encode(publishPacket); var buffer = serializationHelper.Encode(publishPacket);
@@ -329,17 +328,17 @@ namespace MQTTnet.Tests.Formatter
Assert.IsNotNull(publishPacketCopy); Assert.IsNotNull(publishPacketCopy);
Assert.AreEqual(publishPacket.Topic, publishPacketCopy.Topic); Assert.AreEqual(publishPacket.Topic, publishPacketCopy.Topic);
CollectionAssert.AreEqual(publishPacket.Payload, publishPacketCopy.Payload); CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), publishPacketCopy.PayloadSegment.ToArray());
// Now modify the payload and test again. // Now modify the payload and test again.
publishPacket.Payload = Encoding.UTF8.GetBytes("MQTT"); publishPacket.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("MQTT"));
buffer = serializationHelper.Encode(publishPacket); buffer = serializationHelper.Encode(publishPacket);
var publishPacketCopy2 = serializationHelper.Decode(buffer) as MqttPublishPacket; var publishPacketCopy2 = serializationHelper.Decode(buffer) as MqttPublishPacket;
Assert.IsNotNull(publishPacketCopy2); Assert.IsNotNull(publishPacketCopy2);
Assert.AreEqual(publishPacket.Topic, publishPacketCopy2.Topic); Assert.AreEqual(publishPacket.Topic, publishPacketCopy2.Topic);
CollectionAssert.AreEqual(publishPacket.Payload, publishPacketCopy2.Payload); CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), publishPacketCopy2.PayloadSegment.ToArray());
} }
[TestMethod] [TestMethod]
@@ -463,7 +462,7 @@ namespace MQTTnet.Tests.Formatter
PacketIdentifier = 123, PacketIdentifier = 123,
Dup = true, Dup = true,
Retain = true, Retain = true,
Payload = Encoding.ASCII.GetBytes("HELLO"), PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("HELLO")),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
Topic = "A/B/C" Topic = "A/B/C"
}; };
@@ -2,7 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
@@ -296,7 +298,7 @@ namespace MQTTnet.Tests.Formatter
PacketIdentifier = 123, PacketIdentifier = 123,
Dup = true, Dup = true,
Retain = true, Retain = true,
Payload = Encoding.ASCII.GetBytes("Payload"), PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("Payload")),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
Topic = "Topic", Topic = "Topic",
ResponseTopic = "/Response", ResponseTopic = "/Response",
@@ -320,7 +322,7 @@ namespace MQTTnet.Tests.Formatter
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier); Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.AreEqual(publishPacket.Dup, deserialized.Dup); Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
Assert.AreEqual(publishPacket.Retain, deserialized.Retain); Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
CollectionAssert.AreEqual(publishPacket.Payload, deserialized.Payload); CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), deserialized.PayloadSegment.ToArray());
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel); Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
Assert.AreEqual(publishPacket.Topic, deserialized.Topic); Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
Assert.AreEqual(null, deserialized.ResponseTopic); // Not supported in v3.1.1. Assert.AreEqual(null, deserialized.ResponseTopic); // Not supported in v3.1.1.
@@ -2,7 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter; using MQTTnet.Formatter;
@@ -257,7 +259,7 @@ namespace MQTTnet.Tests.Formatter
PacketIdentifier = 123, PacketIdentifier = 123,
Dup = true, Dup = true,
Retain = true, Retain = true,
Payload = Encoding.ASCII.GetBytes("Payload"), PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("Payload")),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
Topic = "Topic", Topic = "Topic",
ResponseTopic = "/Response", ResponseTopic = "/Response",
@@ -281,7 +283,7 @@ namespace MQTTnet.Tests.Formatter
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier); Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.AreEqual(publishPacket.Dup, deserialized.Dup); Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
Assert.AreEqual(publishPacket.Retain, deserialized.Retain); Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
CollectionAssert.AreEqual(publishPacket.Payload, deserialized.Payload); CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), deserialized.PayloadSegment.ToArray());
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel); Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
Assert.AreEqual(publishPacket.Topic, deserialized.Topic); Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
Assert.AreEqual(publishPacket.ResponseTopic, deserialized.ResponseTopic); Assert.AreEqual(publishPacket.ResponseTopic, deserialized.ResponseTopic);
@@ -0,0 +1,48 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MQTTnet.Tests
{
[TestClass]
public class MqttApplicationMessageTest
{
[TestMethod]
public void PayloadSegment()
{
var message = new MqttApplicationMessage();
Assert.AreEqual(0, message.Payload.Length);
Assert.AreEqual(0, message.PayloadSegment.Count);
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.Payload));
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.PayloadSegment.Array));
message.Payload = new byte[] { 1, 2 };
Assert.AreEqual(2, message.Payload.Length);
Assert.AreEqual(2, message.PayloadSegment.Count);
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.Payload));
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.PayloadSegment.Array));
message.Payload = new byte[] { 1, 2, 3 };
Assert.AreEqual(3, message.Payload.Length);
Assert.AreEqual(3, message.PayloadSegment.Count);
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.Payload));
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.PayloadSegment.Array));
message.PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3 });
Assert.AreEqual(3, message.Payload.Length);
Assert.AreEqual(3, message.PayloadSegment.Count);
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.Payload));
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.PayloadSegment.Array));
message.PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3 }, 1, 1);
Assert.AreEqual(1, message.Payload.Length);
Assert.AreEqual(1, message.PayloadSegment.Count);
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.Payload));
Assert.IsFalse(object.ReferenceEquals(message.Payload, message.PayloadSegment.Array));
}
}
}
@@ -164,7 +164,7 @@ namespace MQTTnet.Tests.Server
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(userName, password).Build(); var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(userName, password).Build();
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions)); var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(() => client.ConnectAsync(clientOptions));
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, ex.Result.ResultCode); Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, ex.Result.ResultCode);
} }
} }
@@ -19,7 +19,7 @@ namespace MQTTnet.Formatter
return new MqttApplicationMessage return new MqttApplicationMessage
{ {
Topic = publishPacket.Topic, Topic = publishPacket.Topic,
Payload = publishPacket.Payload, PayloadSegment = publishPacket.PayloadSegment,
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel, QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
Retain = publishPacket.Retain, Retain = publishPacket.Retain,
Dup = publishPacket.Dup, Dup = publishPacket.Dup,
@@ -16,7 +16,7 @@ namespace MQTTnet.Formatter
return new MqttPublishPacket return new MqttPublishPacket
{ {
Topic = publishPacket.Topic, Topic = publishPacket.Topic,
Payload = publishPacket.Payload, PayloadSegment = publishPacket.PayloadSegment,
Retain = publishPacket.Retain, Retain = publishPacket.Retain,
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel, QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
Dup = publishPacket.Dup, Dup = publishPacket.Dup,
@@ -36,7 +36,7 @@ namespace MQTTnet.Formatter
var packet = new MqttPublishPacket var packet = new MqttPublishPacket
{ {
Topic = applicationMessage.Topic, Topic = applicationMessage.Topic,
Payload = applicationMessage.Payload, PayloadSegment = applicationMessage.PayloadSegment,
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel, QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
Retain = applicationMessage.Retain, Retain = applicationMessage.Retain,
Dup = applicationMessage.Dup, Dup = applicationMessage.Dup,
@@ -65,10 +65,16 @@ namespace MQTTnet.Formatter
throw new MqttProtocolViolationException("The CONNECT packet contains no will message (WillFlag)."); throw new MqttProtocolViolationException("The CONNECT packet contains no will message (WillFlag).");
} }
ArraySegment<byte> willMessageBuffer = default;
if (connectPacket.WillMessage?.Length > 0)
{
willMessageBuffer = new ArraySegment<byte>(connectPacket.WillMessage);
}
var packet = new MqttPublishPacket var packet = new MqttPublishPacket
{ {
Topic = connectPacket.WillTopic, Topic = connectPacket.WillTopic,
Payload = connectPacket.WillMessage, PayloadSegment = willMessageBuffer,
QualityOfServiceLevel = connectPacket.WillQoS, QualityOfServiceLevel = connectPacket.WillQoS,
Retain = connectPacket.WillRetain, Retain = connectPacket.WillRetain,
ContentType = connectPacket.WillContentType, ContentType = connectPacket.WillContentType,
@@ -103,9 +103,10 @@ namespace MQTTnet.Formatter.V3
var remainingLength = (uint)(_bufferWriter.Length - 5); var remainingLength = (uint)(_bufferWriter.Length - 5);
var publishPacket = packet as MqttPublishPacket; var publishPacket = packet as MqttPublishPacket;
if (publishPacket?.Payload != null) var payloadSegment = publishPacket?.PayloadSegment;
if (payloadSegment != null)
{ {
remainingLength += (uint)publishPacket.Payload.Length; remainingLength += (uint)payloadSegment.Value.Count;
} }
var remainingLengthSize = MqttBufferWriter.GetVariableByteIntegerSize(remainingLength); var remainingLengthSize = MqttBufferWriter.GetVariableByteIntegerSize(remainingLength);
@@ -119,16 +120,11 @@ namespace MQTTnet.Formatter.V3
_bufferWriter.WriteVariableByteInteger(remainingLength); _bufferWriter.WriteVariableByteInteger(remainingLength);
var buffer = _bufferWriter.GetBuffer(); var buffer = _bufferWriter.GetBuffer();
var firstSegment = new ArraySegment<byte>(buffer, headerOffset, _bufferWriter.Length - headerOffset); var firstSegment = new ArraySegment<byte>(buffer, headerOffset, _bufferWriter.Length - headerOffset);
if (publishPacket?.Payload != null) return payloadSegment == null
{ ? new MqttPacketBuffer(firstSegment)
var payloadSegment = new ArraySegment<byte>(publishPacket.Payload, 0, publishPacket.Payload.Length); : new MqttPacketBuffer(firstSegment, payloadSegment.Value);
return new MqttPacketBuffer(firstSegment, payloadSegment);
}
return new MqttPacketBuffer(firstSegment);
} }
MqttPacket DecodeConnAckPacket(ArraySegment<byte> body) MqttPacket DecodeConnAckPacket(ArraySegment<byte> body)
@@ -282,7 +278,7 @@ namespace MQTTnet.Formatter.V3
if (!_bufferReader.EndOfStream) if (!_bufferReader.EndOfStream)
{ {
packet.Payload = _bufferReader.ReadRemainingData(); packet.PayloadSegment = new ArraySegment<byte>(_bufferReader.ReadRemainingData());
} }
return packet; return packet;
@@ -528,7 +528,7 @@ namespace MQTTnet.Formatter.V5
if (!_bufferReader.EndOfStream) if (!_bufferReader.EndOfStream)
{ {
packet.Payload = _bufferReader.ReadRemainingData(); packet.PayloadSegment = new ArraySegment<byte>(_bufferReader.ReadRemainingData());
} }
return packet; return packet;
@@ -37,9 +37,11 @@ namespace MQTTnet.Formatter.V5
var remainingLength = (uint)_bufferWriter.Length - 5; var remainingLength = (uint)_bufferWriter.Length - 5;
var publishPacket = packet as MqttPublishPacket; var publishPacket = packet as MqttPublishPacket;
if (publishPacket?.Payload != null) var payloadSegment = publishPacket?.PayloadSegment;
if (payloadSegment != null)
{ {
remainingLength += (uint)publishPacket.Payload.Length; remainingLength += (uint)payloadSegment.Value.Count;
} }
var remainingLengthSize = MqttBufferWriter.GetVariableByteIntegerSize(remainingLength); var remainingLengthSize = MqttBufferWriter.GetVariableByteIntegerSize(remainingLength);
@@ -53,16 +55,11 @@ namespace MQTTnet.Formatter.V5
_bufferWriter.WriteVariableByteInteger(remainingLength); _bufferWriter.WriteVariableByteInteger(remainingLength);
var buffer = _bufferWriter.GetBuffer(); var buffer = _bufferWriter.GetBuffer();
var firstSegment = new ArraySegment<byte>(buffer, headerOffset, _bufferWriter.Length - headerOffset); var firstSegment = new ArraySegment<byte>(buffer, headerOffset, _bufferWriter.Length - headerOffset);
if (publishPacket?.Payload != null) return payloadSegment == null
{ ? new MqttPacketBuffer(firstSegment)
var payloadSegment = new ArraySegment<byte>(publishPacket.Payload, 0, publishPacket.Payload.Length); : new MqttPacketBuffer(firstSegment, payloadSegment.Value);
return new MqttPacketBuffer(firstSegment, payloadSegment);
}
return new MqttPacketBuffer(firstSegment);
} }
byte EncodeAuthPacket(MqttAuthPacket packet) byte EncodeAuthPacket(MqttAuthPacket packet)
+52 -4
View File
@@ -2,14 +2,19 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Collections.Generic; using MQTTnet.Internal;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using System;
using System.Collections.Generic;
namespace MQTTnet namespace MQTTnet
{ {
public sealed class MqttApplicationMessage public sealed class MqttApplicationMessage
{ {
byte[] _payloadCache;
ArraySegment<byte> _payloadSegment = EmptyBuffer.ArraySegment;
/// <summary> /// <summary>
/// Gets or sets the content type. /// Gets or sets the content type.
/// The content type must be a UTF-8 encoded string. The content type value identifies the kind of UTF-8 encoded /// The content type must be a UTF-8 encoded string. The content type value identifies the kind of UTF-8 encoded
@@ -48,10 +53,53 @@ namespace MQTTnet
public uint MessageExpiryInterval { get; set; } public uint MessageExpiryInterval { get; set; }
/// <summary> /// <summary>
/// Gets or sets the payload. /// Gets or sets the payload.
/// The payload is the data bytes sent via the MQTT protocol. /// The payload is the data bytes sent via the MQTT protocol.
/// </summary> /// </summary>
public byte[] Payload { get; set; } [Obsolete("Use PayloadSegment instead. This property will be removed in a future release.")]
public byte[] Payload
{
get
{
if (_payloadSegment.Array == null)
{
return null;
}
// just reference from _payloadSegment.Array
if (_payloadSegment.Count == _payloadSegment.Array.Length)
{
return _payloadSegment.Array;
}
// copy from _payloadSegment
if (_payloadCache == null)
{
_payloadCache = new byte[_payloadSegment.Count];
MqttMemoryHelper.Copy(_payloadSegment.Array, _payloadSegment.Offset, _payloadCache, 0, _payloadCache.Length);
}
return _payloadCache;
}
set
{
_payloadCache = null;
_payloadSegment = value == null || value.Length == 0 ? EmptyBuffer.ArraySegment : new ArraySegment<byte>(value);
}
}
/// <summary>
/// Get or set ArraySegment style of Payload.
/// </summary>
public ArraySegment<byte> PayloadSegment
{
get => _payloadSegment;
set
{
_payloadCache = null;
_payloadSegment = value;
}
}
/// <summary> /// <summary>
/// Gets or sets the payload format indicator. /// Gets or sets the payload format indicator.
+58 -43
View File
@@ -2,13 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using MQTTnet.Exceptions;
using MQTTnet.Internal;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet namespace MQTTnet
{ {
@@ -17,7 +20,8 @@ namespace MQTTnet
string _contentType; string _contentType;
byte[] _correlationData; byte[] _correlationData;
uint _messageExpiryInterval; uint _messageExpiryInterval;
byte[] _payload; ArraySegment<byte> _payloadSegment;
MqttPayloadFormatIndicator _payloadFormatIndicator; MqttPayloadFormatIndicator _payloadFormatIndicator;
MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce; MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
string _responseTopic; string _responseTopic;
@@ -37,7 +41,7 @@ namespace MQTTnet
var applicationMessage = new MqttApplicationMessage var applicationMessage = new MqttApplicationMessage
{ {
Topic = _topic, Topic = _topic,
Payload = _payload, PayloadSegment = _payloadSegment,
QualityOfServiceLevel = _qualityOfServiceLevel, QualityOfServiceLevel = _qualityOfServiceLevel,
Retain = _retain, Retain = _retain,
ContentType = _contentType, ContentType = _contentType,
@@ -85,7 +89,15 @@ namespace MQTTnet
public MqttApplicationMessageBuilder WithPayload(byte[] payload) public MqttApplicationMessageBuilder WithPayload(byte[] payload)
{ {
_payload = payload; _payloadSegment = payload == null || payload.Length == 0
? EmptyBuffer.ArraySegment
: new ArraySegment<byte>(payload);
return this;
}
public MqttApplicationMessageBuilder WithPayloadSegment(ArraySegment<byte> payloadSegment)
{
_payloadSegment = payloadSegment;
return this; return this;
} }
@@ -93,70 +105,73 @@ namespace MQTTnet
{ {
if (payload == null) if (payload == null)
{ {
_payload = null; return WithPayload(default(byte[]));
return this;
} }
_payload = payload as byte[] ?? payload.ToArray(); if (payload is byte[] byteArray)
{
return WithPayload(byteArray);
}
return this; if (payload is ArraySegment<byte> arraySegment)
{
return WithPayloadSegment(arraySegment);
}
return WithPayload(payload.ToArray());
} }
public MqttApplicationMessageBuilder WithPayload(Stream payload) public MqttApplicationMessageBuilder WithPayload(Stream payload)
{ {
if (payload == null) return payload == null
{ ? WithPayload(default(byte[]))
_payload = null; : WithPayload(payload, payload.Length - payload.Position);
return this;
}
return WithPayload(payload, payload.Length - payload.Position);
} }
public MqttApplicationMessageBuilder WithPayload(Stream payload, long length) public MqttApplicationMessageBuilder WithPayload(Stream payload, long length)
{ {
if (payload == null) if (payload == null || length == 0)
{ {
_payload = null; return WithPayload(default(byte[]));
return this;
} }
if (payload.Length == 0) var payloadBuffer = new byte[length];
var totalRead = 0;
do
{ {
_payload = null; var bytesRead = payload.Read(payloadBuffer, totalRead, payloadBuffer.Length - totalRead);
} if (bytesRead == 0)
else
{
_payload = new byte[length];
var totalRead = 0;
do
{ {
var bytesRead = payload.Read(_payload, totalRead, _payload.Length - totalRead); break;
if (bytesRead == 0) }
{
break;
}
totalRead += bytesRead; totalRead += bytesRead;
} while (totalRead < length); } while (totalRead < length);
}
return this; return WithPayload(payloadBuffer);
} }
public MqttApplicationMessageBuilder WithPayload(string payload) public MqttApplicationMessageBuilder WithPayload(string payload)
{ {
if (payload == null) if (string.IsNullOrEmpty(payload))
{ {
_payload = null; return WithPayload(default(byte[]));
return this;
} }
_payload = string.IsNullOrEmpty(payload) ? null : Encoding.UTF8.GetBytes(payload); var payloadBuffer = Encoding.UTF8.GetBytes(payload);
return this; return WithPayload(payloadBuffer);
} }
#if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1
public MqttApplicationMessageBuilder WithPayloadSegment(ReadOnlyMemory<byte> payloadSegment)
{
return MemoryMarshal.TryGetArray(payloadSegment, out var segment)
? WithPayloadSegment(segment)
: WithPayload(payloadSegment.ToArray());
}
#endif
/// <summary> /// <summary>
/// Adds the payload format indicator to the message. /// Adds the payload format indicator to the message.
/// <remarks>MQTT 5.0.0+ feature.</remarks> /// <remarks>MQTT 5.0.0+ feature.</remarks>
@@ -11,19 +11,13 @@ namespace MQTTnet
{ {
public static string ConvertPayloadToString(this MqttApplicationMessage applicationMessage) public static string ConvertPayloadToString(this MqttApplicationMessage applicationMessage)
{ {
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); if (applicationMessage == null)
if (applicationMessage.Payload == null)
{ {
return null; throw new ArgumentNullException(nameof(applicationMessage));
} }
if (applicationMessage.Payload.Length == 0) var payloadSegment = applicationMessage.PayloadSegment;
{ return Encoding.UTF8.GetString(payloadSegment.Array, payloadSegment.Offset, payloadSegment.Count);
return string.Empty;
}
return Encoding.UTF8.GetString(applicationMessage.Payload, 0, applicationMessage.Payload.Length);
} }
} }
} }
+4 -3
View File
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using MQTTnet.Protocol; using MQTTnet.Protocol;
@@ -17,10 +18,10 @@ namespace MQTTnet.Packets
public uint MessageExpiryInterval { get; set; } public uint MessageExpiryInterval { get; set; }
public byte[] Payload { get; set; }
public MqttPayloadFormatIndicator PayloadFormatIndicator { get; set; } = MqttPayloadFormatIndicator.Unspecified; public MqttPayloadFormatIndicator PayloadFormatIndicator { get; set; } = MqttPayloadFormatIndicator.Unspecified;
public ArraySegment<byte> PayloadSegment { get; set; }
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } = MqttQualityOfServiceLevel.AtMostOnce; public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } = MqttQualityOfServiceLevel.AtMostOnce;
public string ResponseTopic { get; set; } public string ResponseTopic { get; set; }
@@ -38,7 +39,7 @@ namespace MQTTnet.Packets
public override string ToString() public override string ToString()
{ {
return return
$"Publish: [Topic={Topic}] [Payload.Length={Payload?.Length}] [QoSLevel={QualityOfServiceLevel}] [Dup={Dup}] [Retain={Retain}] [PacketIdentifier={PacketIdentifier}]"; $"Publish: [Topic={Topic}] [PayloadLength={PayloadSegment.Count}] [QoSLevel={QualityOfServiceLevel}] [Dup={Dup}] [Retain={Retain}] [PacketIdentifier={PacketIdentifier}]";
} }
} }
} }
@@ -25,6 +25,7 @@ namespace MQTTnet.Server
if (logger == null) if (logger == null)
throw new ArgumentNullException(nameof(logger)); throw new ArgumentNullException(nameof(logger));
_logger = logger.WithSource(nameof(MqttRetainedMessagesManager)); _logger = logger.WithSource(nameof(MqttRetainedMessagesManager));
} }
@@ -68,7 +69,8 @@ namespace MQTTnet.Server
lock (_messages) lock (_messages)
{ {
var hasPayload = applicationMessage.Payload != null && applicationMessage.Payload.Length > 0; var payloadSegment = applicationMessage.PayloadSegment;
var hasPayload = payloadSegment.Count > 0;
if (!hasPayload) if (!hasPayload)
{ {
@@ -84,8 +86,7 @@ namespace MQTTnet.Server
} }
else else
{ {
if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || !SequenceEqual(existingMessage.PayloadSegment, payloadSegment))
!existingMessage.Payload.SequenceEqual(applicationMessage.Payload ?? EmptyBuffer.Array))
{ {
_messages[applicationMessage.Topic] = applicationMessage; _messages[applicationMessage.Topic] = applicationMessage;
saveIsRequired = true; saveIsRequired = true;
@@ -150,5 +151,14 @@ namespace MQTTnet.Server
await _eventContainer.RetainedMessagesClearedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false); await _eventContainer.RetainedMessagesClearedEvent.InvokeAsync(EventArgs.Empty).ConfigureAwait(false);
} }
} }
private static bool SequenceEqual(ArraySegment<byte> source, ArraySegment<byte> target)
{
#if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1
return source.AsSpan().SequenceEqual(target);
#else
return source.Count == target.Count && Enumerable.SequenceEqual(source, target);
#endif
}
} }
} }
@@ -41,7 +41,7 @@ namespace MQTTnet.Server
new MqttApplicationMessage new MqttApplicationMessage
{ {
Topic = topic, Topic = topic,
Payload = payloadBuffer, PayloadSegment = new ArraySegment<byte>(payloadBuffer),
QualityOfServiceLevel = qualityOfServiceLevel, QualityOfServiceLevel = qualityOfServiceLevel,
Retain = retain Retain = retain
})); }));