mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-18 02:13:00 +08:00
Compare commits
38
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be0c75439c | ||
|
|
9295626503 | ||
|
|
18614d4e81 | ||
|
|
560e80455f | ||
|
|
a97e88be29 | ||
|
|
3b5667c882 | ||
|
|
0790e54794 | ||
|
|
94f9fac2bd | ||
|
|
8ad86928f1 | ||
|
|
d8e5936d45 | ||
|
|
caa717544b | ||
|
|
66b77f8d6d | ||
|
|
ab5264c8ca | ||
|
|
db12309bae | ||
|
|
2953e0dcc5 | ||
|
|
1b18ac9fe3 | ||
|
|
2cdad6622a | ||
|
|
7601d11a7a | ||
|
|
0bb4c6ef09 | ||
|
|
449624fd72 | ||
|
|
99f4f46062 | ||
|
|
b08d11c069 | ||
|
|
ac41e838ec | ||
|
|
836ddf3bbc | ||
|
|
0e815c626c | ||
|
|
4b7d035e9e | ||
|
|
cb3c44f48d | ||
|
|
8b3b9d33de | ||
|
|
59d99509c8 | ||
|
|
2933b76237 | ||
|
|
36c6d8974e | ||
|
|
d6cab76728 | ||
|
|
b4f740f338 | ||
|
|
a004de25ac | ||
|
|
d0584413ca | ||
|
|
ba57537108 | ||
|
|
6dcb110737 | ||
|
|
b599a832da |
@@ -1,10 +1,4 @@
|
|||||||
* [Core] Added net7.0 builds (#1635, thanks to @YAJeff).
|
* [Client] Fixed _PlatformNotSupportedException_ when using Blazor (#1755, thanks to @Nickztar).
|
||||||
* [Client] Added support for passing MQTT v5 options (User properties etc.) for disconnects.
|
* [Server] Fixed _NullReferenceException_ in retained messages management (#1762, thanks to @logicaloud).
|
||||||
* [Client] An internal exception is no longer caught silently when calling _DisconnectAsync_ to indicate that the disconnect is not clean (BREAKING CHANGE).
|
* [Server] Exposed new option which allows disabling packet fragmentation (#1753).
|
||||||
* [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!).
|
* [Server] Expired sessions will no longer be used when a client connects (#1756).
|
||||||
* [Server] Exposed MQTT v5 sent properties from the affected client in _ClientDisconnectedAsync_ event.
|
|
||||||
* [Server] Fixed wrong client ID passed to _InterceptingUnsubscriptionEventArgs_ (#1631, thanks to @ghord).
|
|
||||||
* [Server] Exposed socket settings for TCP keep alive in TCP options (#1544).
|
|
||||||
* [Server] Exposed server session items at server level and allow custom session items for injected application messages (#1638).
|
|
||||||
* [Server] Improved performance for retained message handling when subscribing using "DoNotSendOnSubscribe" or "SendAtSubscribeIfNewSubscriptionOnly" (#1661, thanks to @Int32Overflow).
|
|
||||||
* [Server] Added support for changing the used TLS certificate while the server is running (#1652, thanks to @YAJeff). The certificate provider will now be invoked for every new connection!
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name: CI
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
VERSION: "4.2.0.${{github.run_number}}"
|
VERSION: "4.2.1.${{github.run_number}}"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -50,6 +50,22 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
|||||||
* A custom message interceptor can be added which allows transforming or extending every received application message
|
* A custom message interceptor can be added which allows transforming or extending every received application message
|
||||||
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
MQTTnet is delivered via <b>NuGet</b> package manager. You can find the packages here: https://www.nuget.org/packages/MQTTnet/
|
||||||
|
|
||||||
|
Use these command in the Package Manager console to install MQTTnet manually:
|
||||||
|
```
|
||||||
|
Install-Package MQTTnet
|
||||||
|
```
|
||||||
|
|
||||||
|
Samples for using MQTTnet are part of this repository. For starters these samples are recommended:
|
||||||
|
|
||||||
|
- [Connect with a broker](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Connection_Samples.cs)
|
||||||
|
- [Subscribing to data](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Subscribe_Samples.cs)
|
||||||
|
- [Publishing data](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Publish_Samples.cs)
|
||||||
|
- [Host own broker](https://github.com/dotnet/MQTTnet/blob/master/Samples/Server/Server_Simple_Samples.cs)
|
||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
|
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
|
||||||
|
|||||||
@@ -71,6 +71,32 @@ public static class Client_Connection_Samples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task Connect_With_Amazon_AWS()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample creates a simple MQTT client and connects to an Amazon Web Services broker.
|
||||||
|
*
|
||||||
|
* The broker requires special settings which are set here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
|
{
|
||||||
|
var mqttClientOptions = new MqttClientOptionsBuilder()
|
||||||
|
.WithTcpServer("amazon.web.services.broker")
|
||||||
|
// Disabling packet fragmentation is very important!
|
||||||
|
.WithoutPacketFragmentation()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
|
Console.WriteLine("The MQTT client is connected.");
|
||||||
|
|
||||||
|
await mqttClient.DisconnectAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task Connect_Client_Timeout()
|
public static async Task Connect_Client_Timeout()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
|
using MQTTnet;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public static class PackageInspection_Samples
|
|||||||
.WithTcpServer("broker.hivemq.com")
|
.WithTcpServer("broker.hivemq.com")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
mqttClient.InspectPackage += OnInspectPackage;
|
mqttClient.InspectPacketAsync += OnInspectPacket;
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ public static class PackageInspection_Samples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Task OnInspectPackage(InspectMqttPacketEventArgs eventArgs)
|
static Task OnInspectPacket(InspectMqttPacketEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (eventArgs.Direction == MqttPacketFlowDirection.Inbound)
|
if (eventArgs.Direction == MqttPacketFlowDirection.Inbound)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using MQTTnet.Packets;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Server;
|
namespace MQTTnet.Samples.Server;
|
||||||
|
|
||||||
@@ -32,7 +34,10 @@ public static class Server_Retained_Messages_Samples
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
eventArgs.LoadedRetainedMessages = await JsonSerializer.DeserializeAsync<List<MqttApplicationMessage>>(File.OpenRead(storePath));
|
var models = await JsonSerializer.DeserializeAsync<List<MqttRetainedMessageModel>>(File.OpenRead(storePath)) ?? new List<MqttRetainedMessageModel>();
|
||||||
|
var retainedMessages = models.Select(m => m.ToApplicationMessage()).ToList();
|
||||||
|
|
||||||
|
eventArgs.LoadedRetainedMessages = retainedMessages;
|
||||||
Console.WriteLine("Retained messages loaded.");
|
Console.WriteLine("Retained messages loaded.");
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
@@ -56,7 +61,9 @@ public static class Server_Retained_Messages_Samples
|
|||||||
// used to write all retained messages to dedicated files etc. Then all files must be loaded and a full list
|
// used to write all retained messages to dedicated files etc. Then all files must be loaded and a full list
|
||||||
// of retained messages must be provided in the loaded event.
|
// of retained messages must be provided in the loaded event.
|
||||||
|
|
||||||
var buffer = JsonSerializer.SerializeToUtf8Bytes(eventArgs.StoredRetainedMessages);
|
var models = eventArgs.StoredRetainedMessages.Select(MqttRetainedMessageModel.Create);
|
||||||
|
|
||||||
|
var buffer = JsonSerializer.SerializeToUtf8Bytes(models);
|
||||||
await File.WriteAllBytesAsync(storePath, buffer);
|
await File.WriteAllBytesAsync(storePath, buffer);
|
||||||
Console.WriteLine("Retained messages saved.");
|
Console.WriteLine("Retained messages saved.");
|
||||||
}
|
}
|
||||||
@@ -79,4 +86,59 @@ public static class Server_Retained_Messages_Samples
|
|||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class MqttRetainedMessageModel
|
||||||
|
{
|
||||||
|
public string? ContentType { get; set; }
|
||||||
|
public byte[]? CorrelationData { get; set; }
|
||||||
|
public byte[]? Payload { get; set; }
|
||||||
|
public MqttPayloadFormatIndicator PayloadFormatIndicator { get; set; }
|
||||||
|
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||||
|
public string? ResponseTopic { get; set; }
|
||||||
|
public string? Topic { get; set; }
|
||||||
|
public List<MqttUserProperty>? UserProperties { get; set; }
|
||||||
|
|
||||||
|
public static MqttRetainedMessageModel Create(MqttApplicationMessage message)
|
||||||
|
{
|
||||||
|
if (message == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MqttRetainedMessageModel
|
||||||
|
{
|
||||||
|
Topic = message.Topic,
|
||||||
|
|
||||||
|
// Create a copy of the buffer from the payload segment because
|
||||||
|
// it cannot be serialized and deserialized with the JSON serializer.
|
||||||
|
Payload = message.PayloadSegment.ToArray(),
|
||||||
|
UserProperties = message.UserProperties,
|
||||||
|
ResponseTopic = message.ResponseTopic,
|
||||||
|
CorrelationData = message.CorrelationData,
|
||||||
|
ContentType = message.ContentType,
|
||||||
|
PayloadFormatIndicator = message.PayloadFormatIndicator,
|
||||||
|
QualityOfServiceLevel = message.QualityOfServiceLevel
|
||||||
|
|
||||||
|
// Other properties like "Retain" are not if interest in the storage.
|
||||||
|
// That's why a custom model makes sense.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessage ToApplicationMessage()
|
||||||
|
{
|
||||||
|
return new MqttApplicationMessage
|
||||||
|
{
|
||||||
|
Topic = Topic,
|
||||||
|
PayloadSegment = new ArraySegment<byte>(Payload ?? Array.Empty<byte>()),
|
||||||
|
PayloadFormatIndicator = PayloadFormatIndicator,
|
||||||
|
ResponseTopic = ResponseTopic,
|
||||||
|
CorrelationData = CorrelationData,
|
||||||
|
ContentType = ContentType,
|
||||||
|
UserProperties = UserProperties,
|
||||||
|
QualityOfServiceLevel = QualityOfServiceLevel,
|
||||||
|
Dup = false,
|
||||||
|
Retain = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -2,38 +2,34 @@
|
|||||||
// 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 Microsoft.AspNetCore.Http;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Formatter;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
|
using MQTTnet.Formatter;
|
||||||
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
namespace MQTTnet.AspNetCore
|
||||||
{
|
{
|
||||||
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
|
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
|
||||||
{
|
{
|
||||||
IMqttNetLogger _logger = new MqttNetNullLogger();
|
IMqttNetLogger _logger = MqttNetNullLogger.Instance;
|
||||||
|
|
||||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||||
|
|
||||||
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync()
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
|
if (webSocket == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(webSocket));
|
||||||
|
}
|
||||||
|
|
||||||
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
|
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
|
||||||
|
|
||||||
@@ -48,7 +44,7 @@ namespace MQTTnet.AspNetCore
|
|||||||
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
|
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
|
||||||
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
|
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
|
||||||
|
|
||||||
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _logger))
|
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger))
|
||||||
{
|
{
|
||||||
await clientHandler(channelAdapter).ConfigureAwait(false);
|
await clientHandler(channelAdapter).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -60,8 +56,15 @@ namespace MQTTnet.AspNetCore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
|
|
||||||
var channel = new MemoryMqttChannel(_stream);
|
var channel = new MemoryMqttChannel(_stream);
|
||||||
|
|
||||||
_channelAdapter = new MqttChannelAdapter(channel, serializer, null, new MqttNetEventLogger());
|
_channelAdapter = new MqttChannelAdapter(channel, serializer, new MqttNetEventLogger());
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] Join(params ArraySegment<byte>[] chunks)
|
static byte[] Join(params ArraySegment<byte>[] chunks)
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ namespace MQTTnet.Benchmarks
|
|||||||
|
|
||||||
var writer = new MqttBufferWriter(4096, 65535);
|
var writer = new MqttBufferWriter(4096, 65535);
|
||||||
writer.WriteString("A relative short string.");
|
writer.WriteString("A relative short string.");
|
||||||
writer.WriteBinaryData(_demoPayload);
|
writer.WriteBinary(_demoPayload);
|
||||||
writer.WriteByte(0x01);
|
writer.WriteByte(0x01);
|
||||||
writer.WriteByte(0x02);
|
writer.WriteByte(0x02);
|
||||||
writer.WriteVariableByteInteger(5647382);
|
writer.WriteVariableByteInteger(5647382);
|
||||||
writer.WriteString("A relative short string.");
|
writer.WriteString("A relative short string.");
|
||||||
writer.WriteVariableByteInteger(8574489);
|
writer.WriteVariableByteInteger(8574489);
|
||||||
writer.WriteBinaryData(_demoPayload);
|
writer.WriteBinary(_demoPayload);
|
||||||
writer.WriteByte(2);
|
writer.WriteByte(2);
|
||||||
writer.WriteByte(0x02);
|
writer.WriteByte(0x02);
|
||||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||||
writer.WriteBinaryData(_demoPayload);
|
writer.WriteBinary(_demoPayload);
|
||||||
|
|
||||||
_readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
_readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
||||||
}
|
}
|
||||||
@@ -83,11 +83,11 @@ namespace MQTTnet.Benchmarks
|
|||||||
writer.WriteVariableByteInteger(5647382);
|
writer.WriteVariableByteInteger(5647382);
|
||||||
writer.WriteString("A relative short string.");
|
writer.WriteString("A relative short string.");
|
||||||
writer.WriteVariableByteInteger(8574589);
|
writer.WriteVariableByteInteger(8574589);
|
||||||
writer.WriteBinaryData(_demoPayload);
|
writer.WriteBinary(_demoPayload);
|
||||||
writer.WriteByte(2);
|
writer.WriteByte(2);
|
||||||
writer.WriteByte(0x02);
|
writer.WriteByte(0x02);
|
||||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||||
writer.WriteBinaryData(_demoPayload);
|
writer.WriteBinary(_demoPayload);
|
||||||
|
|
||||||
writer.Reset(0);
|
writer.Reset(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
public void Deserialize_10000_Messages()
|
public void Deserialize_10000_Messages()
|
||||||
{
|
{
|
||||||
var channel = new BenchmarkMqttChannel(_serializedPacket);
|
var channel = new BenchmarkMqttChannel(_serializedPacket);
|
||||||
var reader = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)), null, new MqttNetEventLogger());
|
var reader = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)), new MqttNetEventLogger());
|
||||||
|
|
||||||
for (var i = 0; i < 10000; i++)
|
for (var i = 0; i < 10000; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||||
|
|
||||||
<AssemblyName>MQTTnet.Extensions.ManagedClient</AssemblyName>
|
<AssemblyName>MQTTnet.Extensions.ManagedClient</AssemblyName>
|
||||||
|
|||||||
@@ -353,13 +353,13 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
|
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task HandleSubscriptionExceptionAsync(Exception exception)
|
async Task HandleSubscriptionExceptionAsync(Exception exception, List<MqttTopicFilter> addedSubscriptions, List<string> removedSubscriptions)
|
||||||
{
|
{
|
||||||
_logger.Warning(exception, "Synchronizing subscriptions failed.");
|
_logger.Warning(exception, "Synchronizing subscriptions failed.");
|
||||||
|
|
||||||
if (_synchronizingSubscriptionsFailedEvent.HasHandlers)
|
if (_synchronizingSubscriptionsFailedEvent.HasHandlers)
|
||||||
{
|
{
|
||||||
await _synchronizingSubscriptionsFailedEvent.InvokeAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
|
await _synchronizingSubscriptionsFailedEvent.InvokeAsync(new ManagedProcessFailedEventArgs(exception, addedSubscriptions, removedSubscriptions)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,11 +457,13 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
_logger.Info("Publishing subscriptions at reconnect");
|
_logger.Info("Publishing subscriptions at reconnect");
|
||||||
|
|
||||||
|
List<MqttTopicFilter> topicFilters = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_reconnectSubscriptions.Any())
|
if (_reconnectSubscriptions.Any())
|
||||||
{
|
{
|
||||||
var topicFilters = new List<MqttTopicFilter>();
|
topicFilters = new List<MqttTopicFilter>();
|
||||||
|
|
||||||
foreach (var sub in _reconnectSubscriptions)
|
foreach (var sub in _reconnectSubscriptions)
|
||||||
{
|
{
|
||||||
@@ -479,7 +481,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
|
await HandleSubscriptionExceptionAsync(exception, topicFilters, null).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,6 +593,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
await InternalClient.UnsubscribeAsync(unsubscribeOptionsBuilder.Build()).ConfigureAwait(false);
|
await InternalClient.UnsubscribeAsync(unsubscribeOptionsBuilder.Build()).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//clear because these worked, maybe the subscribe below will fail, only report those
|
||||||
|
removedSubscriptions.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addedSubscriptions != null && addedSubscriptions.Any())
|
if (addedSubscriptions != null && addedSubscriptions.Any())
|
||||||
@@ -607,7 +612,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
|
await HandleSubscriptionExceptionAsync(exception, addedSubscriptions, removedSubscriptions).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the maximum amount of topic filters which will be sent in a SUBSCRIBE/UNSUBSCRIBE packet.
|
/// Defines the maximum amount of topic filters which will be sent in a SUBSCRIBE/UNSUBSCRIBE packet.
|
||||||
/// Amazon AWS limits this number to 8. The default is int.MaxValue.
|
/// Amazon Web Services (AWS) limits this number to 8. The default is int.MaxValue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxTopicFiltersInSubscribeUnsubscribePackets { get; set; } = int.MaxValue;
|
public int MaxTopicFiltersInSubscribeUnsubscribePackets { get; set; } = int.MaxValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,33 @@
|
|||||||
// 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.Packets;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace MQTTnet.Extensions.ManagedClient
|
namespace MQTTnet.Extensions.ManagedClient
|
||||||
{
|
{
|
||||||
public class ManagedProcessFailedEventArgs : EventArgs
|
public class ManagedProcessFailedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public ManagedProcessFailedEventArgs(Exception exception)
|
public ManagedProcessFailedEventArgs(Exception exception, List<MqttTopicFilter> addedSubscriptions, List<string> removedSubscriptions)
|
||||||
{
|
{
|
||||||
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
||||||
|
|
||||||
|
if (addedSubscriptions != null)
|
||||||
|
{
|
||||||
|
AddedSubscriptions = new List<string>(addedSubscriptions.Select(item => item.Topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedSubscriptions != null)
|
||||||
|
{
|
||||||
|
RemovedSubscriptions = new List<string>(removedSubscriptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Exception Exception { get; }
|
public Exception Exception { get; }
|
||||||
|
|
||||||
|
public List<string> AddedSubscriptions { get; }
|
||||||
|
public List<string> RemovedSubscriptions { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,10 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttClient mqttClient = null)
|
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttClient mqttClient = null)
|
||||||
{
|
{
|
||||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
if (factory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(factory));
|
||||||
|
}
|
||||||
|
|
||||||
if (mqttClient == null)
|
if (mqttClient == null)
|
||||||
{
|
{
|
||||||
@@ -24,10 +27,22 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttNetLogger logger)
|
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
if (factory == null)
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
{
|
||||||
|
throw new ArgumentNullException(nameof(factory));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger);
|
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ManagedMqttClientOptionsBuilder CreateManagedMqttClientOptionsBuilder(this MqttFactory _)
|
||||||
|
{
|
||||||
|
return new ManagedMqttClientOptionsBuilder();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||||
|
|
||||||
<AssemblyName>MQTTnet.Extensions.Rpc</AssemblyName>
|
<AssemblyName>MQTTnet.Extensions.Rpc</AssemblyName>
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -68,6 +69,11 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
throw new ArgumentNullException(nameof(methodName));
|
throw new ArgumentNullException(nameof(methodName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_mqttClient.IsConnected)
|
||||||
|
{
|
||||||
|
throw new MqttClientDisconnectedException(null);
|
||||||
|
}
|
||||||
|
|
||||||
var context = new TopicGenerationContext(_mqttClient, _options, methodName, qualityOfServiceLevel);
|
var context = new TopicGenerationContext(_mqttClient, _options, methodName, qualityOfServiceLevel);
|
||||||
var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics(context);
|
var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics(context);
|
||||||
|
|
||||||
@@ -119,7 +125,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 +136,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;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||||
|
|
||||||
<AssemblyName>MQTTnet.Extensions.WebSocket4Net</AssemblyName>
|
<AssemblyName>MQTTnet.Extensions.WebSocket4Net</AssemblyName>
|
||||||
|
|||||||
@@ -27,8 +27,10 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
return new MqttChannelAdapter(
|
return new MqttChannelAdapter(
|
||||||
new MqttTcpChannel(options),
|
new MqttTcpChannel(options),
|
||||||
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
|
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
|
||||||
packetInspector,
|
logger)
|
||||||
logger);
|
{
|
||||||
|
PacketInspector = packetInspector
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case MqttClientWebSocketOptions webSocketOptions:
|
case MqttClientWebSocketOptions webSocketOptions:
|
||||||
@@ -36,8 +38,10 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
return new MqttChannelAdapter(
|
return new MqttChannelAdapter(
|
||||||
new WebSocket4NetMqttChannel(options, webSocketOptions),
|
new WebSocket4NetMqttChannel(options, webSocketOptions),
|
||||||
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
|
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
|
||||||
packetInspector,
|
logger)
|
||||||
logger);
|
{
|
||||||
|
PacketInspector = packetInspector
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,38 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
|||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class ManagedMqttClient_Tests : BaseTestClass
|
public sealed class ManagedMqttClient_Tests : BaseTestClass
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Expose_Custom_Connection_Error()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
server.ValidatingConnectionAsync += args =>
|
||||||
|
{
|
||||||
|
args.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var managedClient = testEnvironment.Factory.CreateManagedMqttClient();
|
||||||
|
|
||||||
|
MqttClientDisconnectedEventArgs disconnectedArgs = null;
|
||||||
|
managedClient.DisconnectedAsync += args =>
|
||||||
|
{
|
||||||
|
disconnectedArgs = args;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var clientOptions = testEnvironment.Factory.CreateManagedMqttClientOptionsBuilder().WithClientOptions(testEnvironment.CreateDefaultClientOptions()).Build();
|
||||||
|
await managedClient.StartAsync(clientOptions);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
Assert.IsNotNull(disconnectedArgs);
|
||||||
|
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, disconnectedArgs.ConnectResult.ResultCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Receive_While_Not_Cleanly_Disconnected()
|
public async Task Receive_While_Not_Cleanly_Disconnected()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,13 +3,16 @@
|
|||||||
// 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;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
|
using MQTTnet.Extensions.Rpc;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
@@ -18,6 +21,38 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class MqttClient_Connection_Tests : BaseTestClass
|
public sealed class MqttClient_Connection_Tests : BaseTestClass
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Connect_Disconnect_Connect()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var clientOptions = testEnvironment.CreateDefaultClientOptions();
|
||||||
|
var client = testEnvironment.CreateClient();
|
||||||
|
|
||||||
|
await client.ConnectAsync(clientOptions);
|
||||||
|
await client.DisconnectAsync();
|
||||||
|
await client.ConnectAsync(clientOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(InvalidOperationException))]
|
||||||
|
public async Task Connect_Multiple_Times_Should_Fail()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var clientOptions = testEnvironment.CreateDefaultClientOptions();
|
||||||
|
var client = testEnvironment.CreateClient();
|
||||||
|
|
||||||
|
await client.ConnectAsync(clientOptions);
|
||||||
|
await client.ConnectAsync(clientOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[ExpectedException(typeof(MqttCommunicationException))]
|
[ExpectedException(typeof(MqttCommunicationException))]
|
||||||
public async Task Connect_To_Invalid_Server_Port_Not_Opened()
|
public async Task Connect_To_Invalid_Server_Port_Not_Opened()
|
||||||
@@ -99,6 +134,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.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
|
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
|
||||||
}
|
}
|
||||||
@@ -125,6 +162,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.AreEqual(MqttDisconnectReasonCode.MessageRateTooHigh, eventArgs.ReasonCode);
|
Assert.AreEqual(MqttDisconnectReasonCode.MessageRateTooHigh, eventArgs.ReasonCode);
|
||||||
}
|
}
|
||||||
@@ -151,6 +190,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);
|
||||||
@@ -158,5 +199,47 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value);
|
Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Return_Non_Success()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
server.ValidatingConnectionAsync += args =>
|
||||||
|
{
|
||||||
|
args.ResponseUserProperties = new List<MqttUserProperty>
|
||||||
|
{
|
||||||
|
new MqttUserProperty("Property", "Value")
|
||||||
|
};
|
||||||
|
|
||||||
|
args.ReasonCode = MqttConnectReasonCode.QuotaExceeded;
|
||||||
|
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = testEnvironment.CreateClient();
|
||||||
|
|
||||||
|
var response = await client.ConnectAsync(testEnvironment.CreateDefaultClientOptionsBuilder().WithoutThrowOnNonSuccessfulConnectResponse().Build());
|
||||||
|
|
||||||
|
Assert.IsNotNull(response);
|
||||||
|
Assert.AreEqual(MqttClientConnectResultCode.QuotaExceeded, response.ResultCode);
|
||||||
|
Assert.AreEqual(response.UserProperties[0].Name, "Property");
|
||||||
|
Assert.AreEqual(response.UserProperties[0].Value, "Value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||||
|
public async Task Rpc_Client_Not_Connected()
|
||||||
|
{
|
||||||
|
var factory = new MqttFactory();
|
||||||
|
using (var client = factory.CreateMqttClient())
|
||||||
|
{
|
||||||
|
var rpcClient = factory.CreateMqttRpcClient(client);
|
||||||
|
await rpcClient.ExecuteAsync("aaa", new byte[] { 1, 2, 3 }, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// 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.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests.Clients.MqttClient
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class MqttClient_Publish_Tests : BaseTestClass
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Acknowledge_After_Disconnect_Should_Fail()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
await client.SubscribeAsync("TEST", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
var flag = false;
|
||||||
|
|
||||||
|
client.ApplicationMessageReceivedAsync += async args =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
args.AutoAcknowledge = false;
|
||||||
|
|
||||||
|
await client.DisconnectAsync();
|
||||||
|
await args.AcknowledgeAsync();
|
||||||
|
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await server.InjectApplicationMessageAsync(
|
||||||
|
new MqttApplicationMessage
|
||||||
|
{
|
||||||
|
Topic = "TEST"
|
||||||
|
});
|
||||||
|
|
||||||
|
SpinWait.SpinUntil(() => flag, TimeSpan.FromSeconds(10));
|
||||||
|
|
||||||
|
Assert.IsTrue(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,37 +70,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Connect_Disconnect_Connect()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
|
||||||
{
|
|
||||||
await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
var clientOptions = testEnvironment.CreateDefaultClientOptions();
|
|
||||||
var client = testEnvironment.CreateClient();
|
|
||||||
|
|
||||||
await client.ConnectAsync(clientOptions);
|
|
||||||
await client.DisconnectAsync();
|
|
||||||
await client.ConnectAsync(clientOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
[ExpectedException(typeof(InvalidOperationException))]
|
|
||||||
public async Task Connect_Multiple_Times_Should_Fail()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
|
||||||
{
|
|
||||||
await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
var clientOptions = testEnvironment.CreateDefaultClientOptions();
|
|
||||||
var client = testEnvironment.CreateClient();
|
|
||||||
|
|
||||||
await client.ConnectAsync(clientOptions);
|
|
||||||
await client.ConnectAsync(clientOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Disconnect_Event_Contains_Exception()
|
public async Task Disconnect_Event_Contains_Exception()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ using MQTTnet.Client;
|
|||||||
namespace MQTTnet.Tests.Clients
|
namespace MQTTnet.Tests.Clients
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class MqttClientOptionsBuilder_Tests
|
public sealed class MqttClientOptionsBuilder_Tests
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void WithConnectionUri_Credential_Test()
|
public void WithConnectionUri_Credential_Test()
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace MQTTnet.Tests.Diagnostics
|
|||||||
|
|
||||||
var packets = new List<string>();
|
var packets = new List<string>();
|
||||||
|
|
||||||
mqttClient.InspectPackage += eventArgs =>
|
mqttClient.InspectPacketAsync += eventArgs =>
|
||||||
{
|
{
|
||||||
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// 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.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
|
||||||
{
|
|
||||||
[TestClass]
|
|
||||||
public class Extension_Tests
|
|
||||||
{
|
|
||||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
|
||||||
[TestMethod]
|
|
||||||
public async Task TimeoutAfter()
|
|
||||||
{
|
|
||||||
await MqttTaskTimeout.WaitAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(500), ct),
|
|
||||||
TimeSpan.FromMilliseconds(100), CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
|
||||||
[TestMethod]
|
|
||||||
public async Task TimeoutAfterWithResult()
|
|
||||||
{
|
|
||||||
await MqttTaskTimeout.WaitAsync(
|
|
||||||
ct => Task.Delay(TimeSpan.FromMilliseconds(500), ct).ContinueWith(t => 5, ct),
|
|
||||||
TimeSpan.FromMilliseconds(100), CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task TimeoutAfterWithInnerException()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await MqttTaskTimeout.WaitAsync(ct => Task.Run(() =>
|
|
||||||
{
|
|
||||||
var iis = new int[0];
|
|
||||||
iis[1] = 0;
|
|
||||||
}, ct), TimeSpan.FromSeconds(1), CancellationToken.None);
|
|
||||||
|
|
||||||
Assert.Fail();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Assert.IsTrue(e is IndexOutOfRangeException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task TimeoutAfterWithInnerExceptionWithResult()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await MqttTaskTimeout.WaitAsync(ct => Task.Run(() =>
|
|
||||||
{
|
|
||||||
var iis = new int[0];
|
|
||||||
iis[1] = 0;
|
|
||||||
return iis[0];
|
|
||||||
}, ct), TimeSpan.FromSeconds(1), CancellationToken.None);
|
|
||||||
|
|
||||||
Assert.Fail();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Assert.IsTrue(e is IndexOutOfRangeException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task TimeoutAfterMemoryUsage()
|
|
||||||
{
|
|
||||||
var initialMemory = GC.GetTotalMemory(true);
|
|
||||||
|
|
||||||
var tasks = Enumerable.Range(0, 100000)
|
|
||||||
.Select(i =>
|
|
||||||
{
|
|
||||||
return MqttTaskTimeout.WaitAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(1), ct),
|
|
||||||
TimeSpan.FromMinutes(1), CancellationToken.None);
|
|
||||||
});
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
|
||||||
AssertIsLess(initialMemory + 2_000_000, GC.GetTotalMemory(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AssertIsLess(long bound, long actual)
|
|
||||||
{
|
|
||||||
if (bound < actual)
|
|
||||||
{
|
|
||||||
Assert.Fail($"value must be less than {bound:N0} but is {actual:N0}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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"
|
||||||
};
|
};
|
||||||
@@ -567,7 +566,6 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
using (var adapter = new MqttChannelAdapter(
|
using (var adapter = new MqttChannelAdapter(
|
||||||
channel,
|
channel,
|
||||||
new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)),
|
new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)),
|
||||||
null,
|
|
||||||
new MqttNetEventLogger()))
|
new MqttNetEventLogger()))
|
||||||
{
|
{
|
||||||
var receivedPacket = adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
var receivedPacket = adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||||
@@ -583,19 +581,12 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter packetFormatterAdapter, byte[] buffer)
|
MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter packetFormatterAdapter, byte[] buffer)
|
||||||
{
|
{
|
||||||
var channel = new MemoryMqttChannel(buffer);
|
var channel = new MemoryMqttChannel(buffer);
|
||||||
var adapter = new MqttChannelAdapter(channel, packetFormatterAdapter, null, new MqttNetEventLogger());
|
var adapter = new MqttChannelAdapter(channel, packetFormatterAdapter, new MqttNetEventLogger());
|
||||||
|
|
||||||
adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||||
return packetFormatterAdapter.ProtocolVersion;
|
return packetFormatterAdapter.ProtocolVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttBufferReader ReaderFactory(byte[] data)
|
|
||||||
{
|
|
||||||
var reader = new MqttBufferReader();
|
|
||||||
reader.SetBuffer(data, 0, data.Length);
|
|
||||||
return reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
TPacket Roundtrip<TPacket>(TPacket packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311, MqttBufferWriter bufferWriter = null) where TPacket : MqttPacket
|
TPacket Roundtrip<TPacket>(TPacket packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311, MqttBufferWriter bufferWriter = null) where TPacket : MqttPacket
|
||||||
{
|
{
|
||||||
var writer = bufferWriter ?? WriterFactory();
|
var writer = bufferWriter ?? WriterFactory();
|
||||||
@@ -604,7 +595,7 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
|
|
||||||
using (var channel = new MemoryMqttChannel(buffer.Join().ToArray()))
|
using (var channel = new MemoryMqttChannel(buffer.Join().ToArray()))
|
||||||
{
|
{
|
||||||
var adapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)), null, new MqttNetEventLogger());
|
var adapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)), new MqttNetEventLogger());
|
||||||
return (TPacket)adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
return (TPacket)adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -76,7 +78,7 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Serialize_Full_MqttConnAckPacket_V310()
|
public void Serialize_Full_MqttConnAckPacket_V310()
|
||||||
{
|
{
|
||||||
var connAckPacket = new MqttConnAckPacket
|
var connAckPacket = new MqttConnAckPacket
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Formatter
|
namespace MQTTnet.Tests.Formatter
|
||||||
@@ -48,22 +49,31 @@ namespace MQTTnet.Tests.Formatter
|
|||||||
Assert.AreEqual(9876U, reader.ReadVariableByteInteger());
|
Assert.AreEqual(9876U, reader.ReadVariableByteInteger());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||||
|
public void Throw_If_String_Too_Long()
|
||||||
|
{
|
||||||
|
var writer = new MqttBufferWriter(4096, 65535);
|
||||||
|
|
||||||
|
writer.WriteString(string.Empty.PadLeft(65536));
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Write_And_Read_Multiple_Times()
|
public void Write_And_Read_Multiple_Times()
|
||||||
{
|
{
|
||||||
var writer = new MqttBufferWriter(4096, 65535);
|
var writer = new MqttBufferWriter(4096, 65535);
|
||||||
writer.WriteString("A relative short string.");
|
writer.WriteString("A relative short string.");
|
||||||
writer.WriteBinaryData(new byte[1234]);
|
writer.WriteBinary(new byte[1234]);
|
||||||
writer.WriteByte(0x01);
|
writer.WriteByte(0x01);
|
||||||
writer.WriteByte(0x02);
|
writer.WriteByte(0x02);
|
||||||
writer.WriteVariableByteInteger(5647382);
|
writer.WriteVariableByteInteger(5647382);
|
||||||
writer.WriteString("A relative short string.");
|
writer.WriteString("A relative short string.");
|
||||||
writer.WriteVariableByteInteger(8574489);
|
writer.WriteVariableByteInteger(8574489);
|
||||||
writer.WriteBinaryData(new byte[48]);
|
writer.WriteBinary(new byte[48]);
|
||||||
writer.WriteByte(2);
|
writer.WriteByte(2);
|
||||||
writer.WriteByte(0x02);
|
writer.WriteByte(0x02);
|
||||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||||
writer.WriteBinaryData(new byte[3]);
|
writer.WriteBinary(new byte[3]);
|
||||||
|
|
||||||
var readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
var readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
||||||
|
|
||||||
|
|||||||
@@ -51,10 +51,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
public void AssertReceivedCountEquals(int expectedCount)
|
public void AssertReceivedCountEquals(int expectedCount)
|
||||||
{
|
{
|
||||||
lock (_receivedEventArgs)
|
Assert.AreEqual(expectedCount, Count);
|
||||||
{
|
|
||||||
Assert.AreEqual(expectedCount, _receivedEventArgs.Count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GeneratePayloadSequence()
|
public string GeneratePayloadSequence()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// 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;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -24,10 +25,9 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
public sealed class TestEnvironment : IDisposable
|
public sealed class TestEnvironment : IDisposable
|
||||||
{
|
{
|
||||||
readonly List<string> _clientErrors = new List<string>();
|
readonly List<string> _clientErrors = new List<string>();
|
||||||
readonly List<IMqttClient> _clients = new List<IMqttClient>();
|
readonly ConcurrentBag<IMqttClient> _clients = new ConcurrentBag<IMqttClient>();
|
||||||
|
|
||||||
readonly List<Exception> _exceptions = new List<Exception>();
|
readonly List<Exception> _exceptions = new List<Exception>();
|
||||||
readonly List<ILowLevelMqttClient> _lowLevelClients = new List<ILowLevelMqttClient>();
|
readonly ConcurrentBag<ILowLevelMqttClient> _lowLevelClients = new ConcurrentBag<ILowLevelMqttClient>();
|
||||||
readonly MqttProtocolVersion _protocolVersion;
|
readonly MqttProtocolVersion _protocolVersion;
|
||||||
readonly List<string> _serverErrors = new List<string>();
|
readonly List<string> _serverErrors = new List<string>();
|
||||||
|
|
||||||
@@ -65,9 +65,12 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
||||||
{
|
{
|
||||||
lock (_clientErrors)
|
if (!IgnoreClientLogErrors)
|
||||||
{
|
{
|
||||||
_clientErrors.Add(e.LogMessage.ToString());
|
lock (_clientErrors)
|
||||||
|
{
|
||||||
|
_clientErrors.Add(e.LogMessage.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -212,30 +215,27 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
public IMqttClient CreateClient()
|
public IMqttClient CreateClient()
|
||||||
{
|
{
|
||||||
lock (_clients)
|
var logger = EnableLogger ? (IMqttNetLogger)ClientLogger : MqttNetNullLogger.Instance;
|
||||||
|
|
||||||
|
var client = Factory.CreateMqttClient(logger);
|
||||||
|
client.ConnectingAsync += e =>
|
||||||
{
|
{
|
||||||
var logger = EnableLogger ? (IMqttNetLogger)ClientLogger : new MqttNetNullLogger();
|
if (TestContext != null)
|
||||||
|
|
||||||
var client = Factory.CreateMqttClient(logger);
|
|
||||||
_clients.Add(client);
|
|
||||||
|
|
||||||
client.ConnectingAsync += e =>
|
|
||||||
{
|
{
|
||||||
if (TestContext != null)
|
var clientOptions = e.ClientOptions;
|
||||||
|
var existingClientId = clientOptions.ClientId;
|
||||||
|
if (existingClientId != null && !existingClientId.StartsWith(TestContext.TestName))
|
||||||
{
|
{
|
||||||
var clientOptions = e.ClientOptions;
|
clientOptions.ClientId = TestContext.TestName + "_" + existingClientId;
|
||||||
var existingClientId = clientOptions.ClientId;
|
|
||||||
if (existingClientId != null && !existingClientId.StartsWith(TestContext.TestName))
|
|
||||||
{
|
|
||||||
clientOptions.ClientId = TestContext.TestName + "_" + existingClientId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
return client;
|
_clients.Add(client);
|
||||||
}
|
|
||||||
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttClientOptions CreateDefaultClientOptions()
|
public MqttClientOptions CreateDefaultClientOptions()
|
||||||
@@ -245,18 +245,15 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
public MqttClientOptionsBuilder CreateDefaultClientOptionsBuilder()
|
public MqttClientOptionsBuilder CreateDefaultClientOptionsBuilder()
|
||||||
{
|
{
|
||||||
return Factory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion).WithTcpServer("127.0.0.1", ServerPort);
|
return Factory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion).WithTcpServer("127.0.0.1", ServerPort).WithClientId(TestContext.TestName + "_" + Guid.NewGuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ILowLevelMqttClient CreateLowLevelClient()
|
public ILowLevelMqttClient CreateLowLevelClient()
|
||||||
{
|
{
|
||||||
lock (_clients)
|
var client = Factory.CreateLowLevelMqttClient(ClientLogger);
|
||||||
{
|
_lowLevelClients.Add(client);
|
||||||
var client = Factory.CreateLowLevelMqttClient(ClientLogger);
|
|
||||||
_lowLevelClients.Add(client);
|
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttServer CreateServer(MqttServerOptions options)
|
public MqttServer CreateServer(MqttServerOptions options)
|
||||||
@@ -383,13 +380,16 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_clientErrors)
|
if (!IgnoreClientLogErrors)
|
||||||
{
|
{
|
||||||
if (!IgnoreClientLogErrors && _clientErrors.Count > 0)
|
lock (_clientErrors)
|
||||||
{
|
{
|
||||||
var message = $"Client(s) had {_clientErrors.Count} errors (${string.Join(Environment.NewLine, _clientErrors)})";
|
if (_clientErrors.Count > 0)
|
||||||
Console.WriteLine(message);
|
{
|
||||||
throw new Exception(message);
|
var message = $"Client(s) had {_clientErrors.Count} errors (${string.Join(Environment.NewLine, _clientErrors)})";
|
||||||
|
Console.WriteLine(message);
|
||||||
|
throw new Exception(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@ namespace MQTTnet.Tests
|
|||||||
{
|
{
|
||||||
var formatterAdapter = new MqttPacketFormatterAdapter(_protocolVersion, new MqttBufferWriter(4096, 65535));
|
var formatterAdapter = new MqttPacketFormatterAdapter(_protocolVersion, new MqttBufferWriter(4096, 65535));
|
||||||
|
|
||||||
var adapter = new MqttChannelAdapter(channel, formatterAdapter, null, MqttNetNullLogger.Instance);
|
var adapter = new MqttChannelAdapter(channel, formatterAdapter, MqttNetNullLogger.Instance);
|
||||||
return adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
return adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ namespace MQTTnet.Tests
|
|||||||
writer.WriteString("1234567890");
|
writer.WriteString("1234567890");
|
||||||
Assert.AreEqual(10 + 2, writer.Length);
|
Assert.AreEqual(10 + 2, writer.Length);
|
||||||
|
|
||||||
writer.WriteBinaryData(new byte[300]);
|
writer.WriteBinary(new byte[300]);
|
||||||
Assert.AreEqual(300 + 2 + 12, writer.Length);
|
Assert.AreEqual(300 + 2 + 12, writer.Length);
|
||||||
|
|
||||||
writer.WriteBinaryData(new byte[5000]);
|
writer.WriteBinary(new byte[5000]);
|
||||||
Assert.AreEqual(5000 + 2 + 300 + 2 + 12, writer.Length);
|
Assert.AreEqual(5000 + 2 + 300 + 2 + 12, writer.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -598,8 +598,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
// c
|
// c
|
||||||
var c1 = await testEnvironment.ConnectClient(clientOptionsBuilder);
|
var c1 = await testEnvironment.ConnectClient(clientOptionsBuilder);
|
||||||
|
|
||||||
await Task.Delay(500);
|
await LongTestDelay();
|
||||||
|
|
||||||
var flow = string.Join(string.Empty, events);
|
var flow = string.Join(string.Empty, events);
|
||||||
Assert.AreEqual("c", flow);
|
Assert.AreEqual("c", flow);
|
||||||
|
|
||||||
@@ -607,10 +606,8 @@ namespace MQTTnet.Tests.Server
|
|||||||
// Connect client with same client ID. Should disconnect existing client.
|
// Connect client with same client ID. Should disconnect existing client.
|
||||||
var c2 = await testEnvironment.ConnectClient(clientOptionsBuilder);
|
var c2 = await testEnvironment.ConnectClient(clientOptionsBuilder);
|
||||||
|
|
||||||
await Task.Delay(500);
|
await LongTestDelay();
|
||||||
|
|
||||||
flow = string.Join(string.Empty, events);
|
flow = string.Join(string.Empty, events);
|
||||||
|
|
||||||
Assert.AreEqual("cdc", flow);
|
Assert.AreEqual("cdc", flow);
|
||||||
|
|
||||||
c2.ApplicationMessageReceivedAsync += e =>
|
c2.ApplicationMessageReceivedAsync += e =>
|
||||||
@@ -628,8 +625,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
// r
|
// r
|
||||||
await c2.PublishStringAsync("topic");
|
await c2.PublishStringAsync("topic");
|
||||||
|
|
||||||
await Task.Delay(500);
|
await LongTestDelay();
|
||||||
|
|
||||||
flow = string.Join(string.Empty, events);
|
flow = string.Join(string.Empty, events);
|
||||||
Assert.AreEqual("cdcr", flow);
|
Assert.AreEqual("cdcr", flow);
|
||||||
|
|
||||||
@@ -639,16 +635,17 @@ namespace MQTTnet.Tests.Server
|
|||||||
await c1.TryDisconnectAsync();
|
await c1.TryDisconnectAsync();
|
||||||
Assert.AreEqual(false, c1.IsConnected);
|
Assert.AreEqual(false, c1.IsConnected);
|
||||||
|
|
||||||
await Task.Delay(500);
|
await LongTestDelay();
|
||||||
|
|
||||||
// d
|
// d
|
||||||
Assert.AreEqual(true, c2.IsConnected);
|
Assert.AreEqual(true, c2.IsConnected);
|
||||||
await c2.DisconnectAsync();
|
await c2.DisconnectAsync();
|
||||||
|
|
||||||
await Task.Delay(500);
|
await LongTestDelay();
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
flow = string.Join(string.Empty, events);
|
flow = string.Join(string.Empty, events);
|
||||||
Assert.AreEqual("cdcrd", flow);
|
Assert.AreEqual("cdcrd", flow);
|
||||||
}
|
}
|
||||||
@@ -852,11 +849,11 @@ namespace MQTTnet.Tests.Server
|
|||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.Delay(100);
|
// Stopping the server should disconnect the connection with the client
|
||||||
|
// which, it turn, will fire the disconnected event at the client.
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
|
|
||||||
await Task.Delay(100);
|
await LongTestDelay();
|
||||||
|
|
||||||
Assert.AreEqual(1, disconnectCalled);
|
Assert.AreEqual(1, disconnectCalled);
|
||||||
}
|
}
|
||||||
@@ -954,7 +951,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
var client = testEnvironment.CreateClient();
|
var client = testEnvironment.CreateClient();
|
||||||
|
|
||||||
client.InspectPackage += e =>
|
client.InspectPacketAsync += e =>
|
||||||
{
|
{
|
||||||
if (e.Buffer.Length > 0)
|
if (e.Buffer.Length > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests.Server
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class MqttRetainedMessageManager_Tests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task MqttRetainedMessageManager_GetUndefinedTopic()
|
||||||
|
{
|
||||||
|
var logger = new Mockups.TestLogger();
|
||||||
|
var eventContainer = new MQTTnet.Server.MqttServerEventContainer();
|
||||||
|
var retainedMessagesManager = new MQTTnet.Server.MqttRetainedMessagesManager(eventContainer, logger);
|
||||||
|
var task = retainedMessagesManager.GetMessage("undefined");
|
||||||
|
Assert.IsNotNull(task, "Task should not be null");
|
||||||
|
var result = await task;
|
||||||
|
Assert.IsNull(result, "Null result expected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -221,14 +221,17 @@ namespace MQTTnet.Tests.Server
|
|||||||
var eventContainer = new MqttServerEventContainer();
|
var eventContainer = new MqttServerEventContainer();
|
||||||
var clientSessionManager = new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger);
|
var clientSessionManager = new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger);
|
||||||
|
|
||||||
var session = new MqttSession("", false, new ConcurrentDictionary<object, object>(), options, eventContainer, retainedMessagesManager, clientSessionManager);
|
var session = new MqttSession(new MqttConnectPacket
|
||||||
|
{
|
||||||
|
ClientId = ""
|
||||||
|
}, new ConcurrentDictionary<object, object>(), options, eventContainer, retainedMessagesManager, clientSessionManager);
|
||||||
|
|
||||||
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerEventContainer(), retainedMessagesManager, clientSessionManager);
|
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerEventContainer(), retainedMessagesManager, clientSessionManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
|
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
|
||||||
{
|
{
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out _, out _);
|
MqttTopicHash.Calculate(topic, out var topicHash, out _, out _);
|
||||||
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
|
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// 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.Threading.Tasks;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Formatter;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
|
||||||
{
|
|
||||||
[TestClass]
|
|
||||||
public sealed class Publish_Tests : BaseTestClass
|
|
||||||
{
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Return_Success_When_Subscribed()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
|
||||||
{
|
|
||||||
await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
var sender = await testEnvironment.ConnectClient();
|
|
||||||
var receiver = await testEnvironment.ConnectClient();
|
|
||||||
|
|
||||||
await receiver.SubscribeAsync("A");
|
|
||||||
|
|
||||||
// AtLeastOnce is required to get an ACK packet from the server.
|
|
||||||
var publishResult = await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
|
||||||
|
|
||||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, publishResult.ReasonCode);
|
|
||||||
|
|
||||||
Assert.AreEqual(true, publishResult.IsSuccess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Return_NoMatchingSubscribers_When_Not_Subscribed()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
|
||||||
{
|
|
||||||
await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
var sender = await testEnvironment.ConnectClient();
|
|
||||||
var receiver = await testEnvironment.ConnectClient();
|
|
||||||
|
|
||||||
await receiver.SubscribeAsync("A");
|
|
||||||
|
|
||||||
// AtLeastOnce is required to get an ACK packet from the server.
|
|
||||||
var publishResult = await sender.PublishStringAsync("B", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
|
||||||
|
|
||||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, publishResult.ReasonCode);
|
|
||||||
|
|
||||||
Assert.AreEqual(true, publishResult.IsSuccess);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Formatter;
|
||||||
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
@@ -27,5 +29,118 @@ namespace MQTTnet.Tests.Server
|
|||||||
await client.PublishStringAsync("test", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
await client.PublishStringAsync("test", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Return_NoMatchingSubscribers_When_Not_Subscribed()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var sender = await testEnvironment.ConnectClient();
|
||||||
|
var receiver = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
await receiver.SubscribeAsync("A");
|
||||||
|
|
||||||
|
// AtLeastOnce is required to get an ACK packet from the server.
|
||||||
|
var publishResult = await sender.PublishStringAsync("B", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, publishResult.ReasonCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(true, publishResult.IsSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Return_Success_When_Subscribed()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var sender = await testEnvironment.ConnectClient();
|
||||||
|
var receiver = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
await receiver.SubscribeAsync("A");
|
||||||
|
|
||||||
|
// AtLeastOnce is required to get an ACK packet from the server.
|
||||||
|
var publishResult = await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttClientPublishReasonCode.Success, publishResult.ReasonCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(true, publishResult.IsSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Intercept_Client_Enqueue()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var sender = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
var receiver = await testEnvironment.ConnectClient();
|
||||||
|
await receiver.SubscribeAsync("A");
|
||||||
|
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||||
|
|
||||||
|
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
receivedMessages.AssertReceivedCountEquals(1);
|
||||||
|
|
||||||
|
server.InterceptingClientEnqueueAsync += e =>
|
||||||
|
{
|
||||||
|
e.AcceptEnqueue = false;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// Do not increase because the internal enqueue to the target client is not accepted!
|
||||||
|
receivedMessages.AssertReceivedCountEquals(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Intercept_Client_Enqueue_Multiple_Clients_Subscribed_Messages_Are_Filtered()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var sender = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
var receiverOne = await testEnvironment.ConnectClient(o => o.WithClientId("One"));
|
||||||
|
await receiverOne.SubscribeAsync("A");
|
||||||
|
var receivedMessagesOne = testEnvironment.CreateApplicationMessageHandler(receiverOne);
|
||||||
|
|
||||||
|
var receiverTwo = await testEnvironment.ConnectClient(o => o.WithClientId("Two"));
|
||||||
|
await receiverTwo.SubscribeAsync("A");
|
||||||
|
var receivedMessagesTwo = testEnvironment.CreateApplicationMessageHandler(receiverTwo);
|
||||||
|
|
||||||
|
var receiverThree = await testEnvironment.ConnectClient(o => o.WithClientId("Three"));
|
||||||
|
await receiverThree.SubscribeAsync("A");
|
||||||
|
var receivedMessagesThree = testEnvironment.CreateApplicationMessageHandler(receiverThree);
|
||||||
|
|
||||||
|
server.InterceptingClientEnqueueAsync += e =>
|
||||||
|
{
|
||||||
|
if (e.ReceiverClientId.Contains("Two")) e.AcceptEnqueue = false;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
receivedMessagesOne.AssertReceivedCountEquals(1);
|
||||||
|
receivedMessagesTwo.AssertReceivedCountEquals(0);
|
||||||
|
receivedMessagesThree.AssertReceivedCountEquals(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,28 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Do_Not_Use_Expired_Session()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||||
|
|
||||||
|
var c1 = await testEnvironment.ConnectClient(o => o.WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3));
|
||||||
|
|
||||||
|
// Kill the client connection and ensure that the session will stay there but is expired after 3 seconds!
|
||||||
|
c1.Dispose();
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(6));
|
||||||
|
|
||||||
|
c1 = testEnvironment.CreateClient();
|
||||||
|
var options = testEnvironment.CreateDefaultClientOptionsBuilder().WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3).Build();
|
||||||
|
|
||||||
|
var connectResult = await c1.ConnectAsync(options);
|
||||||
|
Assert.AreEqual(false, connectResult.IsSessionPresent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Fire_Deleted_Event()
|
public async Task Fire_Deleted_Event()
|
||||||
{
|
{
|
||||||
@@ -134,16 +156,12 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
await testEnvironment.StartServer();
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(1)).WithProtocolVersion(protocolVersion);
|
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(10)).WithProtocolVersion(protocolVersion);
|
||||||
|
|
||||||
var hasReceive = false;
|
var hasReceive = false;
|
||||||
|
|
||||||
void OnReceive()
|
void OnReceive()
|
||||||
{
|
{
|
||||||
if (!hasReceive)
|
hasReceive = true;
|
||||||
{
|
|
||||||
hasReceive = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to connect 50 clients at the same time.
|
// Try to connect 50 clients at the same time.
|
||||||
@@ -356,7 +374,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
|
||||||
{
|
{
|
||||||
await client.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, timeout.Token).ConfigureAwait(false);
|
await client.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, timeout.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,29 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Intercept_Subscribe_With_User_Properties()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
InterceptingSubscriptionEventArgs eventArgs = null;
|
||||||
|
server.InterceptingSubscriptionAsync += e =>
|
||||||
|
{
|
||||||
|
eventArgs = e;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
var subscribeOptions = testEnvironment.Factory.CreateSubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
|
||||||
|
await client.SubscribeAsync(subscribeOptions);
|
||||||
|
|
||||||
|
CollectionAssert.AreEqual(subscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||||
public async Task Disconnect_While_Subscribing()
|
public async Task Disconnect_While_Subscribing()
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
var topics = t.Value;
|
var topics = t.Value;
|
||||||
foreach (var topic in topics)
|
foreach (var topic in topics)
|
||||||
{
|
{
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
MqttTopicHash.Calculate(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
||||||
|
|
||||||
bucketDepths.TryGetValue(topicHash, out var currentValue);
|
bucketDepths.TryGetValue(topicHash, out var currentValue);
|
||||||
++currentValue;
|
++currentValue;
|
||||||
@@ -96,7 +96,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
{
|
{
|
||||||
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/#";
|
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/#";
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
{
|
{
|
||||||
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/+";
|
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/+";
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
// UInt64 is limited to 8 levels
|
// UInt64 is limited to 8 levels
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
var topic = sb.ToString();
|
var topic = sb.ToString();
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not present");
|
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not present");
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
var topic = sb.ToString();
|
var topic = sb.ToString();
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
var l3 = "#";
|
var l3 = "#";
|
||||||
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
|
|
||||||
@@ -354,7 +354,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
var l3 = "prop1";
|
var l3 = "prop1";
|
||||||
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not wildcard present");
|
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not wildcard present");
|
||||||
|
|
||||||
@@ -447,7 +447,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
var l3 = "prop1";
|
var l3 = "prop1";
|
||||||
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
|
|
||||||
@@ -475,7 +475,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
void CheckTopicHash(string topic, ulong expectedHash, ulong expectedHashMask)
|
void CheckTopicHash(string topic, ulong expectedHash, ulong expectedHashMask)
|
||||||
{
|
{
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
MqttTopicHash.Calculate(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
||||||
|
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("Topic: " + topic);
|
Console.WriteLine("Topic: " + topic);
|
||||||
@@ -501,7 +501,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
{
|
{
|
||||||
var topicToFind = topicsToFind[countDown];
|
var topicToFind = topicsToFind[countDown];
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(topicToFind, out var topicHash, out _, out _);
|
MqttTopicHash.Calculate(topicToFind, out var topicHash, out _, out _);
|
||||||
|
|
||||||
clientSession.TryCheckSubscriptions(topicToFind, topicHash, MqttQualityOfServiceLevel.AtMostOnce, "OtherClient", out var result);
|
clientSession.TryCheckSubscriptions(topicToFind, topicHash, MqttQualityOfServiceLevel.AtMostOnce, "OtherClient", out var result);
|
||||||
if (result.IsSubscribed)
|
if (result.IsSubscribed)
|
||||||
@@ -557,7 +557,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
var eventContainer = new MqttServerEventContainer();
|
var eventContainer = new MqttServerEventContainer();
|
||||||
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
|
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
|
||||||
var sessionManager = new MqttClientSessionsManager(serverOptions, retainedMessagesManager, eventContainer, logger);
|
var sessionManager = new MqttClientSessionsManager(serverOptions, retainedMessagesManager, eventContainer, logger);
|
||||||
_clientSession = new MqttSession(clientId, false, new Dictionary<object, object>(), serverOptions, eventContainer, retainedMessagesManager, sessionManager);
|
_clientSession = new MqttSession(new MqttConnectPacket{ ClientId = clientId }, new Dictionary<object, object>(), serverOptions, eventContainer, retainedMessagesManager, sessionManager);
|
||||||
|
|
||||||
List<string> topicsToSubscribe;
|
List<string> topicsToSubscribe;
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,14 @@
|
|||||||
// 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.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Formatter;
|
||||||
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
{
|
{
|
||||||
@@ -28,5 +32,28 @@ namespace MQTTnet.Tests.Server
|
|||||||
await client.UnsubscribeAsync("#");
|
await client.UnsubscribeAsync("#");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Intercept_Unsubscribe_With_User_Properties()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
InterceptingUnsubscriptionEventArgs eventArgs = null;
|
||||||
|
server.InterceptingUnsubscriptionAsync += e =>
|
||||||
|
{
|
||||||
|
eventArgs = e;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
var unsubscribeOptions = testEnvironment.Factory.CreateUnsubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
|
||||||
|
await client.UnsubscribeAsync(unsubscribeOptions);
|
||||||
|
|
||||||
|
CollectionAssert.AreEqual(unsubscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,18 +150,8 @@ namespace MQTTnet.Tests
|
|||||||
{
|
{
|
||||||
Assert.AreEqual(expectedResult, MqttTopicFilterComparer.Compare(topic, filter));
|
Assert.AreEqual(expectedResult, MqttTopicFilterComparer.Compare(topic, filter));
|
||||||
|
|
||||||
ulong topicHash;
|
MqttTopicHash.Calculate(topic, out var topicHash, out _, out _);
|
||||||
ulong topicHashMask;
|
MqttTopicHash.Calculate(filter, out var filterTopicHash, out var filterTopicHashMask, out _);
|
||||||
bool topicHasWildcard;
|
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
|
||||||
|
|
||||||
|
|
||||||
ulong filterTopicHash;
|
|
||||||
ulong filterTopicHashMask;
|
|
||||||
bool filterTopicHasWildcard;
|
|
||||||
|
|
||||||
MqttSubscription.CalculateTopicHash(filter, out filterTopicHash, out filterTopicHashMask, out filterTopicHasWildcard);
|
|
||||||
|
|
||||||
if (expectedResult == MqttTopicFilterCompareResult.IsMatch)
|
if (expectedResult == MqttTopicFilterCompareResult.IsMatch)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,18 +27,15 @@ namespace MQTTnet.Adapter
|
|||||||
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||||
readonly MqttNetSourceLogger _logger;
|
readonly MqttNetSourceLogger _logger;
|
||||||
|
|
||||||
readonly MqttPacketInspector _packetInspector;
|
|
||||||
|
|
||||||
readonly byte[] _singleByteBuffer = new byte[1];
|
readonly byte[] _singleByteBuffer = new byte[1];
|
||||||
|
|
||||||
readonly AsyncLock _syncRoot = new AsyncLock();
|
readonly AsyncLock _syncRoot = new AsyncLock();
|
||||||
|
|
||||||
Statistics _statistics; // mutable struct, don't make readonly!
|
Statistics _statistics; // mutable struct, don't make readonly!
|
||||||
|
|
||||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, MqttPacketInspector packetInspector, IMqttNetLogger logger)
|
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
||||||
_packetInspector = packetInspector;
|
|
||||||
|
|
||||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||||
|
|
||||||
@@ -64,6 +61,10 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||||
|
|
||||||
|
public MqttPacketInspector PacketInspector { get; set; }
|
||||||
|
|
||||||
|
public bool AllowPacketFragmentation { get; set; } = true;
|
||||||
|
|
||||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
@@ -126,7 +127,7 @@ namespace MQTTnet.Adapter
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_packetInspector?.BeginReceivePacket();
|
PacketInspector?.BeginReceivePacket();
|
||||||
|
|
||||||
ReceivedMqttPacket receivedPacket;
|
ReceivedMqttPacket receivedPacket;
|
||||||
var receivedPacketTask = ReceiveAsync(cancellationToken);
|
var receivedPacketTask = ReceiveAsync(cancellationToken);
|
||||||
@@ -144,7 +145,7 @@ namespace MQTTnet.Adapter
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_packetInspector?.EndReceivePacket();
|
PacketInspector?.EndReceivePacket();
|
||||||
|
|
||||||
Interlocked.Add(ref _statistics._bytesSent, receivedPacket.TotalLength);
|
Interlocked.Add(ref _statistics._bytesSent, receivedPacket.TotalLength);
|
||||||
|
|
||||||
@@ -180,7 +181,10 @@ namespace MQTTnet.Adapter
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetStatistics() => _statistics.Reset();
|
public void ResetStatistics()
|
||||||
|
{
|
||||||
|
_statistics.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -197,18 +201,18 @@ namespace MQTTnet.Adapter
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var packetBuffer = PacketFormatterAdapter.Encode(packet);
|
var packetBuffer = PacketFormatterAdapter.Encode(packet);
|
||||||
_packetInspector?.BeginSendPacket(packetBuffer);
|
PacketInspector?.BeginSendPacket(packetBuffer);
|
||||||
|
|
||||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetBuffer.Length, packet);
|
_logger.Verbose("TX ({0} bytes) >>> {1}", packetBuffer.Length, packet);
|
||||||
|
|
||||||
if (packetBuffer.Payload.Count > 0)
|
if (packetBuffer.Payload.Count == 0 || !AllowPacketFragmentation)
|
||||||
{
|
{
|
||||||
await _channel.WriteAsync(packetBuffer.Packet, false, cancellationToken).ConfigureAwait(false);
|
await _channel.WriteAsync(packetBuffer.Join(), true, cancellationToken).ConfigureAwait(false);
|
||||||
await _channel.WriteAsync(packetBuffer.Payload, true, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _channel.WriteAsync(packetBuffer.Packet, true, cancellationToken).ConfigureAwait(false);
|
await _channel.WriteAsync(packetBuffer.Packet, false, cancellationToken).ConfigureAwait(false);
|
||||||
|
await _channel.WriteAsync(packetBuffer.Payload, true, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Interlocked.Add(ref _statistics._bytesReceived, packetBuffer.Length);
|
Interlocked.Add(ref _statistics._bytesReceived, packetBuffer.Length);
|
||||||
@@ -270,7 +274,7 @@ namespace MQTTnet.Adapter
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_packetInspector?.FillReceiveBuffer(_singleByteBuffer);
|
PacketInspector?.FillReceiveBuffer(_singleByteBuffer);
|
||||||
|
|
||||||
encodedByte = _singleByteBuffer[0];
|
encodedByte = _singleByteBuffer[0];
|
||||||
|
|
||||||
@@ -323,7 +327,7 @@ namespace MQTTnet.Adapter
|
|||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
_packetInspector?.FillReceiveBuffer(buffer);
|
PacketInspector?.FillReceiveBuffer(buffer);
|
||||||
|
|
||||||
var hasRemainingLength = buffer[1] != 0;
|
var hasRemainingLength = buffer[1] != 0;
|
||||||
if (!hasRemainingLength)
|
if (!hasRemainingLength)
|
||||||
@@ -407,7 +411,7 @@ namespace MQTTnet.Adapter
|
|||||||
bodyOffset += readBytes;
|
bodyOffset += readBytes;
|
||||||
} while (bodyOffset < bodyLength);
|
} while (bodyOffset < bodyLength);
|
||||||
|
|
||||||
_packetInspector?.FillReceiveBuffer(body);
|
PacketInspector?.FillReceiveBuffer(body);
|
||||||
|
|
||||||
var bodySegment = new ArraySegment<byte>(body, 0, bodyLength);
|
var bodySegment = new ArraySegment<byte>(body, 0, bodyLength);
|
||||||
return new ReceivedMqttPacket(fixedHeader.Flags, bodySegment, fixedHeader.TotalLength);
|
return new ReceivedMqttPacket(fixedHeader.Flags, bodySegment, fixedHeader.TotalLength);
|
||||||
@@ -455,7 +459,7 @@ namespace MQTTnet.Adapter
|
|||||||
throw new MqttCommunicationException(exception);
|
throw new MqttCommunicationException(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct Statistics
|
struct Statistics
|
||||||
{
|
{
|
||||||
public long _bytesReceived;
|
public long _bytesReceived;
|
||||||
public long _bytesSent;
|
public long _bytesSent;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync;
|
event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync;
|
||||||
|
|
||||||
event Func<InspectMqttPacketEventArgs, Task> InspectPackage;
|
event Func<InspectMqttPacketEventArgs, Task> InspectPacketAsync;
|
||||||
|
|
||||||
bool IsConnected { get; }
|
bool IsConnected { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using MQTTnet.Diagnostics;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
|
namespace MQTTnet.Client.Internal
|
||||||
|
{
|
||||||
|
public sealed class MqttClientEvents
|
||||||
|
{
|
||||||
|
public AsyncEvent<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceivedEvent { get; } = new AsyncEvent<MqttApplicationMessageReceivedEventArgs>();
|
||||||
|
public AsyncEvent<MqttClientConnectedEventArgs> ConnectedEvent { get; } = new AsyncEvent<MqttClientConnectedEventArgs>();
|
||||||
|
public AsyncEvent<MqttClientConnectingEventArgs> ConnectingEvent { get; } = new AsyncEvent<MqttClientConnectingEventArgs>();
|
||||||
|
public AsyncEvent<MqttClientDisconnectedEventArgs> DisconnectedEvent { get; } = new AsyncEvent<MqttClientDisconnectedEventArgs>();
|
||||||
|
public AsyncEvent<InspectMqttPacketEventArgs> InspectPacketEvent { get; } = new AsyncEvent<InspectMqttPacketEventArgs>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace MQTTnet.Client.Internal
|
||||||
|
{
|
||||||
|
public static class MqttClientResultFactory
|
||||||
|
{
|
||||||
|
public static readonly MqttClientConnectResultFactory ConnectResult = new MqttClientConnectResultFactory();
|
||||||
|
public static readonly MqttClientPublishResultFactory PublishResult = new MqttClientPublishResultFactory();
|
||||||
|
public static readonly MqttClientSubscribeResultFactory SubscribeResult = new MqttClientSubscribeResultFactory();
|
||||||
|
public static readonly MqttClientUnsubscribeResultFactory UnsubscribeResult = new MqttClientUnsubscribeResultFactory();
|
||||||
|
}
|
||||||
|
}
|
||||||
+207
-160
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
|
using MQTTnet.Client.Internal;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
@@ -20,17 +21,9 @@ namespace MQTTnet.Client
|
|||||||
public sealed class MqttClient : Disposable, IMqttClient
|
public sealed class MqttClient : Disposable, IMqttClient
|
||||||
{
|
{
|
||||||
readonly IMqttClientAdapterFactory _adapterFactory;
|
readonly IMqttClientAdapterFactory _adapterFactory;
|
||||||
readonly AsyncEvent<MqttApplicationMessageReceivedEventArgs> _applicationMessageReceivedEvent = new AsyncEvent<MqttApplicationMessageReceivedEventArgs>();
|
|
||||||
|
|
||||||
readonly MqttClientPublishResultFactory _clientPublishResultFactory = new MqttClientPublishResultFactory();
|
|
||||||
readonly MqttClientSubscribeResultFactory _clientSubscribeResultFactory = new MqttClientSubscribeResultFactory();
|
|
||||||
readonly MqttClientUnsubscribeResultFactory _clientUnsubscribeResultFactory = new MqttClientUnsubscribeResultFactory();
|
|
||||||
|
|
||||||
readonly AsyncEvent<MqttClientConnectedEventArgs> _connectedEvent = new AsyncEvent<MqttClientConnectedEventArgs>();
|
|
||||||
readonly AsyncEvent<MqttClientConnectingEventArgs> _connectingEvent = new AsyncEvent<MqttClientConnectingEventArgs>();
|
|
||||||
readonly AsyncEvent<MqttClientDisconnectedEventArgs> _disconnectedEvent = new AsyncEvent<MqttClientDisconnectedEventArgs>();
|
|
||||||
readonly object _disconnectLock = new object();
|
readonly object _disconnectLock = new object();
|
||||||
readonly AsyncEvent<InspectMqttPacketEventArgs> _inspectPacketEvent = new AsyncEvent<InspectMqttPacketEventArgs>();
|
readonly MqttClientEvents _events = new MqttClientEvents();
|
||||||
readonly MqttNetSourceLogger _logger;
|
readonly MqttNetSourceLogger _logger;
|
||||||
|
|
||||||
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
|
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
|
||||||
@@ -65,32 +58,32 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
public event Func<MqttApplicationMessageReceivedEventArgs, Task> ApplicationMessageReceivedAsync
|
public event Func<MqttApplicationMessageReceivedEventArgs, Task> ApplicationMessageReceivedAsync
|
||||||
{
|
{
|
||||||
add => _applicationMessageReceivedEvent.AddHandler(value);
|
add => _events.ApplicationMessageReceivedEvent.AddHandler(value);
|
||||||
remove => _applicationMessageReceivedEvent.RemoveHandler(value);
|
remove => _events.ApplicationMessageReceivedEvent.RemoveHandler(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Func<MqttClientConnectedEventArgs, Task> ConnectedAsync
|
public event Func<MqttClientConnectedEventArgs, Task> ConnectedAsync
|
||||||
{
|
{
|
||||||
add => _connectedEvent.AddHandler(value);
|
add => _events.ConnectedEvent.AddHandler(value);
|
||||||
remove => _connectedEvent.RemoveHandler(value);
|
remove => _events.ConnectedEvent.RemoveHandler(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Func<MqttClientConnectingEventArgs, Task> ConnectingAsync
|
public event Func<MqttClientConnectingEventArgs, Task> ConnectingAsync
|
||||||
{
|
{
|
||||||
add => _connectingEvent.AddHandler(value);
|
add => _events.ConnectingEvent.AddHandler(value);
|
||||||
remove => _connectingEvent.RemoveHandler(value);
|
remove => _events.ConnectingEvent.RemoveHandler(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync
|
public event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync
|
||||||
{
|
{
|
||||||
add => _disconnectedEvent.AddHandler(value);
|
add => _events.DisconnectedEvent.AddHandler(value);
|
||||||
remove => _disconnectedEvent.RemoveHandler(value);
|
remove => _events.DisconnectedEvent.RemoveHandler(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Func<InspectMqttPacketEventArgs, Task> InspectPackage
|
public event Func<InspectMqttPacketEventArgs, Task> InspectPacketAsync
|
||||||
{
|
{
|
||||||
add => _inspectPacketEvent.AddHandler(value);
|
add => _events.InspectPacketEvent.AddHandler(value);
|
||||||
remove => _inspectPacketEvent.RemoveHandler(value);
|
remove => _events.InspectPacketEvent.RemoveHandler(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsConnected => (MqttClientConnectionStatus)_connectionStatus == MqttClientConnectionStatus.Connected;
|
public bool IsConnected => (MqttClientConnectionStatus)_connectionStatus == MqttClientConnectionStatus.Connected;
|
||||||
@@ -114,9 +107,9 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
Options = options;
|
Options = options;
|
||||||
|
|
||||||
if (_connectingEvent.HasHandlers)
|
if (_events.ConnectingEvent.HasHandlers)
|
||||||
{
|
{
|
||||||
await _connectingEvent.InvokeAsync(new MqttClientConnectingEventArgs(options));
|
await _events.ConnectingEvent.InvokeAsync(new MqttClientConnectingEventArgs(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
Cleanup();
|
Cleanup();
|
||||||
@@ -127,12 +120,12 @@ namespace MQTTnet.Client
|
|||||||
_mqttClientAlive = new CancellationTokenSource();
|
_mqttClientAlive = new CancellationTokenSource();
|
||||||
var mqttClientAliveToken = _mqttClientAlive.Token;
|
var mqttClientAliveToken = _mqttClientAlive.Token;
|
||||||
|
|
||||||
var adapter = _adapterFactory.CreateClientAdapter(options, new MqttPacketInspector(_inspectPacketEvent, _rootLogger), _rootLogger);
|
var adapter = _adapterFactory.CreateClientAdapter(options, new MqttPacketInspector(_events.InspectPacketEvent, _rootLogger), _rootLogger);
|
||||||
_adapter = adapter;
|
_adapter = adapter ?? throw new InvalidOperationException("The adapter factory did not provide an adapter.");
|
||||||
|
|
||||||
if (cancellationToken.CanBeCanceled)
|
if (cancellationToken.CanBeCanceled)
|
||||||
{
|
{
|
||||||
connectResult = await ConnectInternal(cancellationToken).ConfigureAwait(false);
|
connectResult = await ConnectInternal(adapter, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -140,43 +133,50 @@ namespace MQTTnet.Client
|
|||||||
// CancellationToken.None or similar.
|
// CancellationToken.None or similar.
|
||||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||||
{
|
{
|
||||||
connectResult = await ConnectInternal(timeout.Token).ConfigureAwait(false);
|
connectResult = await ConnectInternal(adapter, timeout.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (connectResult.ResultCode != MqttClientConnectResultCode.Success)
|
||||||
|
{
|
||||||
|
_logger.Warning("Connecting failed: {0}", connectResult.ResultCode);
|
||||||
|
return connectResult;
|
||||||
|
}
|
||||||
|
|
||||||
_lastPacketSentTimestamp = DateTime.UtcNow;
|
_lastPacketSentTimestamp = DateTime.UtcNow;
|
||||||
|
|
||||||
var keepAliveInterval = Options.KeepAlivePeriod;
|
var keepAliveInterval = Options.KeepAlivePeriod;
|
||||||
if (connectResult.ServerKeepAlive > 0)
|
if (connectResult.ServerKeepAlive > 0)
|
||||||
{
|
{
|
||||||
_logger.Info($"Using keep alive value ({connectResult.ServerKeepAlive}) sent from the server.");
|
_logger.Info($"Using keep alive value ({connectResult.ServerKeepAlive}) sent from the server");
|
||||||
keepAliveInterval = TimeSpan.FromSeconds(connectResult.ServerKeepAlive);
|
keepAliveInterval = TimeSpan.FromSeconds(connectResult.ServerKeepAlive);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keepAliveInterval != TimeSpan.Zero)
|
if (keepAliveInterval != TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(mqttClientAliveToken), mqttClientAliveToken);
|
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessages(mqttClientAliveToken), mqttClientAliveToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connected, MqttClientConnectionStatus.Connecting);
|
CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connected, MqttClientConnectionStatus.Connecting);
|
||||||
|
|
||||||
_logger.Info("Connected.");
|
_logger.Info("Connected");
|
||||||
|
|
||||||
if (_connectedEvent.HasHandlers)
|
await OnConnected(connectResult).ConfigureAwait(false);
|
||||||
{
|
|
||||||
var eventArgs = new MqttClientConnectedEventArgs(connectResult);
|
|
||||||
await _connectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return connectResult;
|
return connectResult;
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
|
if (exception is MqttConnectingFailedException connectingFailedException)
|
||||||
|
{
|
||||||
|
connectResult = connectingFailedException.Result;
|
||||||
|
}
|
||||||
|
|
||||||
_disconnectReason = (int)MqttClientDisconnectOptionsReason.UnspecifiedError;
|
_disconnectReason = (int)MqttClientDisconnectOptionsReason.UnspecifiedError;
|
||||||
|
|
||||||
_logger.Error(exception, "Error while connecting with server.");
|
_logger.Error(exception, "Error while connecting with server");
|
||||||
|
|
||||||
await DisconnectInternalAsync(null, exception, connectResult).ConfigureAwait(false);
|
await DisconnectInternal(null, exception, connectResult).ConfigureAwait(false);
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
@@ -219,19 +219,19 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
if (cancellationToken.CanBeCanceled)
|
if (cancellationToken.CanBeCanceled)
|
||||||
{
|
{
|
||||||
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
|
await Send(disconnectPacket, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||||
{
|
{
|
||||||
await SendAsync(disconnectPacket, timeout.Token).ConfigureAwait(false);
|
await Send(disconnectPacket, timeout.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await DisconnectCoreAsync(null, null, null, clientWasConnected).ConfigureAwait(false);
|
await DisconnectCore(null, null, null, clientWasConnected).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,13 +239,13 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
if (cancellationToken.CanBeCanceled)
|
if (cancellationToken.CanBeCanceled)
|
||||||
{
|
{
|
||||||
await SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken).ConfigureAwait(false);
|
await Request<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||||
{
|
{
|
||||||
await SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, timeout.Token).ConfigureAwait(false);
|
await Request<MqttPingRespPacket>(MqttPingReqPacket.Instance, timeout.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,11 +274,11 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
case MqttQualityOfServiceLevel.AtLeastOnce:
|
case MqttQualityOfServiceLevel.AtLeastOnce:
|
||||||
{
|
{
|
||||||
return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
|
return PublishAtLeastOnce(publishPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
case MqttQualityOfServiceLevel.ExactlyOnce:
|
case MqttQualityOfServiceLevel.ExactlyOnce:
|
||||||
{
|
{
|
||||||
return PublishExactlyOnceAsync(publishPacket, cancellationToken);
|
return PublishExactlyOnce(publishPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
@@ -306,7 +306,7 @@ namespace MQTTnet.Client
|
|||||||
UserProperties = data.UserProperties
|
UserProperties = data.UserProperties
|
||||||
};
|
};
|
||||||
|
|
||||||
return SendAsync(authPacket, cancellationToken);
|
return Send(authPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken = default)
|
public async Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken = default)
|
||||||
@@ -335,17 +335,17 @@ namespace MQTTnet.Client
|
|||||||
MqttSubAckPacket subAckPacket;
|
MqttSubAckPacket subAckPacket;
|
||||||
if (cancellationToken.CanBeCanceled)
|
if (cancellationToken.CanBeCanceled)
|
||||||
{
|
{
|
||||||
subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, cancellationToken).ConfigureAwait(false);
|
subAckPacket = await Request<MqttSubAckPacket>(subscribePacket, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||||
{
|
{
|
||||||
subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, timeout.Token).ConfigureAwait(false);
|
subAckPacket = await Request<MqttSubAckPacket>(subscribePacket, timeout.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _clientSubscribeResultFactory.Create(subscribePacket, subAckPacket);
|
return MqttClientResultFactory.SubscribeResult.Create(subscribePacket, subAckPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken = default)
|
public async Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken = default)
|
||||||
@@ -374,17 +374,17 @@ namespace MQTTnet.Client
|
|||||||
MqttUnsubAckPacket unsubAckPacket;
|
MqttUnsubAckPacket unsubAckPacket;
|
||||||
if (cancellationToken.CanBeCanceled)
|
if (cancellationToken.CanBeCanceled)
|
||||||
{
|
{
|
||||||
unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, cancellationToken).ConfigureAwait(false);
|
unsubAckPacket = await Request<MqttUnsubAckPacket>(unsubscribePacket, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||||
{
|
{
|
||||||
unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, timeout.Token).ConfigureAwait(false);
|
unsubAckPacket = await Request<MqttUnsubAckPacket>(unsubscribePacket, timeout.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _clientUnsubscribeResultFactory.Create(unsubscribePacket, unsubAckPacket);
|
return MqttClientResultFactory.UnsubscribeResult.Create(unsubscribePacket, unsubAckPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
@@ -408,7 +408,7 @@ namespace MQTTnet.Client
|
|||||||
if (!eventArgs.ProcessingFailed)
|
if (!eventArgs.ProcessingFailed)
|
||||||
{
|
{
|
||||||
var pubAckPacket = MqttPacketFactories.PubAck.Create(eventArgs);
|
var pubAckPacket = MqttPacketFactories.PubAck.Create(eventArgs);
|
||||||
return SendAsync(pubAckPacket, cancellationToken);
|
return Send(pubAckPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||||
@@ -416,7 +416,7 @@ namespace MQTTnet.Client
|
|||||||
if (!eventArgs.ProcessingFailed)
|
if (!eventArgs.ProcessingFailed)
|
||||||
{
|
{
|
||||||
var pubRecPacket = MqttPacketFactories.PubRec.Create(eventArgs);
|
var pubRecPacket = MqttPacketFactories.PubRec.Create(eventArgs);
|
||||||
return SendAsync(pubRecPacket, cancellationToken);
|
return Send(pubRecPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -427,27 +427,51 @@ namespace MQTTnet.Client
|
|||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttClientConnectResult> AuthenticateAsync(MqttClientOptions options, CancellationToken cancellationToken)
|
async Task<MqttClientConnectResult> Authenticate(IMqttChannelAdapter channelAdapter, MqttClientOptions options, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
MqttClientConnectResult result;
|
MqttClientConnectResult result;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var connectPacket = MqttPacketFactories.Connect.Create(options);
|
var connectPacket = MqttPacketFactories.Connect.Create(options);
|
||||||
|
await Send(connectPacket, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
|
var receivedPacket = await Receive(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var clientConnectResultFactory = new MqttClientConnectResultFactory();
|
if (receivedPacket is MqttConnAckPacket connAckPacket)
|
||||||
result = clientConnectResultFactory.Create(connAckPacket, _adapter.PacketFormatterAdapter.ProtocolVersion);
|
{
|
||||||
|
result = MqttClientResultFactory.ConnectResult.Create(connAckPacket, _adapter.PacketFormatterAdapter.ProtocolVersion);
|
||||||
|
}
|
||||||
|
else if (receivedPacket is MqttAuthPacket)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Extended authentication handler is not yet supported");
|
||||||
|
}
|
||||||
|
else if (receivedPacket == null)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException("Connection closed.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Received an unexpected MQTT packet ({receivedPacket}).");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
throw new MqttConnectingFailedException($"Error while authenticating. {exception.Message}", exception, null);
|
throw new MqttConnectingFailedException($"Error while authenticating. {exception.Message}", exception, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.ResultCode != MqttClientConnectResultCode.Success)
|
// This is no feature. It is basically a backward compatibility option and should be removed in the future.
|
||||||
|
// The client should not throw any exception if the transport layer connection was successful and the server
|
||||||
|
// did send a proper ACK packet with a non success response.
|
||||||
|
if (options.ThrowOnNonSuccessfulConnectResponse)
|
||||||
{
|
{
|
||||||
throw new MqttConnectingFailedException($"Connecting with MQTT server failed ({result.ResultCode}).", null, result);
|
_logger.Warning(
|
||||||
|
"Client will now throw an _MqttConnectingFailedException_. This is obsolete and will be removed in the future. Consider setting _ThrowOnNonSuccessfulResponseFromServer=False_ in client options.");
|
||||||
|
|
||||||
|
if (result.ResultCode != MqttClientConnectResultCode.Success)
|
||||||
|
{
|
||||||
|
throw new MqttConnectingFailedException($"Connecting with MQTT server failed ({result.ResultCode}).", null, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Verbose("Authenticated MQTT connection with server established.");
|
_logger.Verbose("Authenticated MQTT connection with server established.");
|
||||||
@@ -482,27 +506,31 @@ namespace MQTTnet.Client
|
|||||||
return (MqttClientConnectionStatus)Interlocked.CompareExchange(ref _connectionStatus, (int)value, (int)comparand);
|
return (MqttClientConnectionStatus)Interlocked.CompareExchange(ref _connectionStatus, (int)value, (int)comparand);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttClientConnectResult> ConnectInternal(CancellationToken cancellationToken)
|
async Task<MqttClientConnectResult> ConnectInternal(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var backgroundCancellationToken = _mqttClientAlive.Token;
|
var backgroundCancellationToken = _mqttClientAlive.Token;
|
||||||
|
|
||||||
using (var effectiveCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken))
|
using (var effectiveCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken))
|
||||||
{
|
{
|
||||||
_logger.Verbose("Trying to connect with server '{0}'.", Options.ChannelOptions);
|
_logger.Verbose("Trying to connect with server '{0}'", Options.ChannelOptions);
|
||||||
await _adapter.ConnectAsync(effectiveCancellationToken.Token).ConfigureAwait(false);
|
await _adapter.ConnectAsync(effectiveCancellationToken.Token).ConfigureAwait(false);
|
||||||
_logger.Verbose("Connection with server established.");
|
_logger.Verbose("Connection with server established");
|
||||||
|
|
||||||
_publishPacketReceiverQueue?.Dispose();
|
_publishPacketReceiverQueue?.Dispose();
|
||||||
_publishPacketReceiverQueue = new AsyncQueue<MqttPublishPacket>();
|
_publishPacketReceiverQueue = new AsyncQueue<MqttPublishPacket>();
|
||||||
|
|
||||||
_publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken);
|
var connectResult = await Authenticate(channelAdapter, Options, effectiveCancellationToken.Token).ConfigureAwait(false);
|
||||||
_packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken);
|
if (connectResult.ResultCode == MqttClientConnectResultCode.Success)
|
||||||
|
{
|
||||||
|
_publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken);
|
||||||
|
_packetReceiverTask = Task.Run(() => ReceivePacketsLoop(backgroundCancellationToken), backgroundCancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
return await AuthenticateAsync(Options, effectiveCancellationToken.Token).ConfigureAwait(false);
|
return connectResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task DisconnectCoreAsync(Task sender, Exception exception, MqttClientConnectResult connectResult, bool clientWasConnected)
|
async Task DisconnectCore(Task sender, Exception exception, MqttClientConnectResult connectResult, bool clientWasConnected)
|
||||||
{
|
{
|
||||||
TryInitiateDisconnect();
|
TryInitiateDisconnect();
|
||||||
|
|
||||||
@@ -527,7 +555,7 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_packetDispatcher.Dispose(new MqttClientDisconnectedException(exception));
|
_packetDispatcher?.Dispose(new MqttClientDisconnectedException(exception));
|
||||||
|
|
||||||
var receiverTask = _packetReceiverTask.WaitAsync(sender, _logger);
|
var receiverTask = _packetReceiverTask.WaitAsync(sender, _logger);
|
||||||
var publishPacketReceiverTask = _publishPacketReceiverTask.WaitAsync(sender, _logger);
|
var publishPacketReceiverTask = _publishPacketReceiverTask.WaitAsync(sender, _logger);
|
||||||
@@ -557,17 +585,17 @@ 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(() => _disconnectedEvent.InvokeAsync(eventArgs)).RunInBackground(_logger);
|
Task.Run(() => _events.DisconnectedEvent.InvokeAsync(eventArgs)).RunInBackground(_logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientConnectResult connectResult)
|
Task DisconnectInternal(Task sender, Exception exception, MqttClientConnectResult connectResult)
|
||||||
{
|
{
|
||||||
var clientWasConnected = IsConnected;
|
var clientWasConnected = IsConnected;
|
||||||
|
|
||||||
if (!DisconnectIsPendingOrFinished())
|
if (!DisconnectIsPendingOrFinished())
|
||||||
{
|
{
|
||||||
return DisconnectCoreAsync(sender, exception, connectResult, clientWasConnected);
|
return DisconnectCore(sender, exception, connectResult, clientWasConnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
@@ -612,15 +640,26 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
|
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessage(MqttPublishPacket publishPacket)
|
||||||
{
|
{
|
||||||
var applicationMessage = MqttApplicationMessageFactory.Create(publishPacket);
|
var applicationMessage = MqttApplicationMessageFactory.Create(publishPacket);
|
||||||
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage, publishPacket, AcknowledgeReceivedPublishPacket);
|
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage, publishPacket, AcknowledgeReceivedPublishPacket);
|
||||||
await _applicationMessageReceivedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
await _events.ApplicationMessageReceivedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||||
|
|
||||||
return eventArgs;
|
return eventArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task OnConnected(MqttClientConnectResult connectResult)
|
||||||
|
{
|
||||||
|
if (_events.ConnectedEvent.HasHandlers)
|
||||||
|
{
|
||||||
|
var eventArgs = new MqttClientConnectedEventArgs(connectResult);
|
||||||
|
return _events.ConnectedEvent.InvokeAsync(eventArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket)
|
Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket)
|
||||||
{
|
{
|
||||||
var extendedAuthenticationExchangeHandler = Options.ExtendedAuthenticationExchangeHandler;
|
var extendedAuthenticationExchangeHandler = Options.ExtendedAuthenticationExchangeHandler;
|
||||||
@@ -641,7 +680,7 @@ namespace MQTTnet.Client
|
|||||||
// Also dispatch disconnect to waiting threads to generate a proper exception.
|
// Also dispatch disconnect to waiting threads to generate a proper exception.
|
||||||
_packetDispatcher.Dispose(new MqttClientUnexpectedDisconnectReceivedException(disconnectPacket));
|
_packetDispatcher.Dispose(new MqttClientUnexpectedDisconnectReceivedException(disconnectPacket));
|
||||||
|
|
||||||
return DisconnectInternalAsync(_packetReceiverTask, null, null);
|
return DisconnectInternal(_packetReceiverTask, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task ProcessReceivedPublishPackets(CancellationToken cancellationToken)
|
async Task ProcessReceivedPublishPackets(CancellationToken cancellationToken)
|
||||||
@@ -657,7 +696,7 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
var publishPacket = publishPacketDequeueResult.Item;
|
var publishPacket = publishPacketDequeueResult.Item;
|
||||||
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
|
var eventArgs = await HandleReceivedApplicationMessage(publishPacket).ConfigureAwait(false);
|
||||||
|
|
||||||
if (eventArgs.AutoAcknowledge)
|
if (eventArgs.AutoAcknowledge)
|
||||||
{
|
{
|
||||||
@@ -681,7 +720,7 @@ namespace MQTTnet.Client
|
|||||||
// The packet is unknown. Probably due to a restart of the client.
|
// 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.
|
// So wen send this to the server to trigger a full resend of the message.
|
||||||
var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound);
|
var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound);
|
||||||
return SendAsync(pubRelPacket, cancellationToken);
|
return Send(pubRelPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
@@ -690,39 +729,115 @@ namespace MQTTnet.Client
|
|||||||
Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
|
Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var pubCompPacket = MqttPacketFactories.PubComp.Create(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
var pubCompPacket = MqttPacketFactories.PubComp.Create(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||||
return SendAsync(pubCompPacket, cancellationToken);
|
return Send(pubCompPacket, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttClientPublishResult> PublishAtLeastOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
async Task<MqttClientPublishResult> PublishAtLeastOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||||
|
|
||||||
var pubAckPacket = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
var pubAckPacket = await Request<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||||
return _clientPublishResultFactory.Create(pubAckPacket);
|
return MqttClientResultFactory.PublishResult.Create(pubAckPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
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]
|
||||||
await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false);
|
await Send(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return _clientPublishResultFactory.Create(null);
|
return MqttClientResultFactory.PublishResult.Create(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
async Task<MqttClientPublishResult> PublishExactlyOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||||
|
|
||||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
var pubRecPacket = await Request<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||||
|
|
||||||
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
var pubCompPacket = await Request<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return _clientPublishResultFactory.Create(pubRecPacket, pubCompPacket);
|
return MqttClientResultFactory.PublishResult.Create(pubRecPacket, pubCompPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttPacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttPacket
|
async Task<MqttPacket> Receive(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var packetTask = _adapter.ReceivePacketAsync(cancellationToken);
|
||||||
|
|
||||||
|
MqttPacket packet;
|
||||||
|
if (packetTask.IsCompleted)
|
||||||
|
{
|
||||||
|
packet = packetTask.Result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
packet = await packetTask.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task ReceivePacketsLoop(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Verbose("Start receiving packets.");
|
||||||
|
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var packet = await Receive(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet == null)
|
||||||
|
{
|
||||||
|
await DisconnectInternal(_packetReceiverTask, null, null).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await TryProcessReceivedPacket(packet, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
if (_cleanDisconnectInitiated)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception is AggregateException aggregateException)
|
||||||
|
{
|
||||||
|
exception = aggregateException.GetBaseException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception is OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if (exception is MqttCommunicationException)
|
||||||
|
{
|
||||||
|
_logger.Warning(exception, "Communication error while receiving packets.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Error(exception, "Error while receiving packets.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_packetDispatcher.FailAll(exception);
|
||||||
|
|
||||||
|
await DisconnectInternal(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_logger.Verbose("Stopped receiving packets.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<TResponsePacket> Request<TResponsePacket>(MqttPacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttPacket
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
@@ -736,7 +851,7 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await SendAsync(requestPacket, cancellationToken).ConfigureAwait(false);
|
await Send(requestPacket, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
@@ -760,12 +875,13 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Task SendAsync(MqttPacket packet, CancellationToken cancellationToken)
|
Task Send(MqttPacket packet, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
_lastPacketSentTimestamp = DateTime.UtcNow;
|
_lastPacketSentTimestamp = DateTime.UtcNow;
|
||||||
|
|
||||||
|
//ThrowIfNotConnected();
|
||||||
return _adapter.SendPacketAsync(packet, cancellationToken);
|
return _adapter.SendPacketAsync(packet, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,7 +939,7 @@ namespace MQTTnet.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task TryProcessReceivedPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
async Task TryProcessReceivedPacket(MqttPacket packet, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -884,80 +1000,11 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
_packetDispatcher.FailAll(exception);
|
_packetDispatcher.FailAll(exception);
|
||||||
|
|
||||||
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
await DisconnectInternal(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task TryReceivePacketsAsync(CancellationToken cancellationToken)
|
async Task TrySendKeepAliveMessages(CancellationToken cancellationToken)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.Verbose("Start receiving packets.");
|
|
||||||
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
MqttPacket packet;
|
|
||||||
var packetTask = _adapter.ReceivePacketAsync(cancellationToken);
|
|
||||||
|
|
||||||
if (packetTask.IsCompleted)
|
|
||||||
{
|
|
||||||
packet = packetTask.Result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
packet = await packetTask.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet == null)
|
|
||||||
{
|
|
||||||
await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await TryProcessReceivedPacketAsync(packet, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
if (_cleanDisconnectInitiated)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exception is AggregateException aggregateException)
|
|
||||||
{
|
|
||||||
exception = aggregateException.GetBaseException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exception is OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if (exception is MqttCommunicationException)
|
|
||||||
{
|
|
||||||
_logger.Warning(exception, "Communication error while receiving packets.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Error(exception, "Error while receiving packets.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_packetDispatcher.FailAll(exception);
|
|
||||||
|
|
||||||
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_logger.Verbose("Stopped receiving packets.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task TrySendKeepAliveMessagesAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -1006,7 +1053,7 @@ namespace MQTTnet.Client
|
|||||||
_logger.Error(exception, "Error exception while sending/receiving keep alive packets.");
|
_logger.Error(exception, "Error exception while sending/receiving keep alive packets.");
|
||||||
}
|
}
|
||||||
|
|
||||||
await DisconnectInternalAsync(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
|
await DisconnectInternal(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
public sealed class MqttClientOptions
|
public sealed class MqttClientOptions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Usually the MQTT packets can be send partially. This is done by using multiple TCP packets
|
||||||
|
/// or WebSocket frames etc. Unfortunately not all brokers (like Amazon Web Services (AWS)) do support this feature and
|
||||||
|
/// will close the connection when receiving such packets. If such a service is used this flag must
|
||||||
|
/// be set to _false_.
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowPacketFragmentation { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the authentication data.
|
/// Gets or sets the authentication data.
|
||||||
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
@@ -92,6 +100,11 @@ namespace MQTTnet.Client
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public uint SessionExpiryInterval { get; set; }
|
public uint SessionExpiryInterval { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether an exception should be thrown when the server has sent a non success ACK packet.
|
||||||
|
/// </summary>
|
||||||
|
public bool ThrowOnNonSuccessfulConnectResponse { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the timeout which will be applied at socket level and internal operations.
|
/// Gets or sets the timeout which will be applied at socket level and internal operations.
|
||||||
/// The default value is the same as for sockets in .NET in general.
|
/// The default value is the same as for sockets in .NET in general.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Licensed to the .NET Foundation under one or more agreements.
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
// 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.
|
||||||
|
|
||||||
@@ -79,6 +79,16 @@ namespace MQTTnet.Client
|
|||||||
return _options;
|
return _options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The client will not throw an exception when the MQTT server responses with a non success ACK packet.
|
||||||
|
/// This will become the default behavior in future versions of the library.
|
||||||
|
/// </summary>
|
||||||
|
public MqttClientOptionsBuilder WithoutThrowOnNonSuccessfulConnectResponse()
|
||||||
|
{
|
||||||
|
_options.ThrowOnNonSuccessfulConnectResponse = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithAuthentication(string method, byte[] data)
|
public MqttClientOptionsBuilder WithAuthentication(string method, byte[] data)
|
||||||
{
|
{
|
||||||
_options.AuthenticationMethod = method;
|
_options.AuthenticationMethod = method;
|
||||||
@@ -86,12 +96,24 @@ namespace MQTTnet.Client
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean session is used in MQTT versions below 5.0.0. It is the same as setting "CleanStart".
|
||||||
|
/// </summary>
|
||||||
public MqttClientOptionsBuilder WithCleanSession(bool value = true)
|
public MqttClientOptionsBuilder WithCleanSession(bool value = true)
|
||||||
{
|
{
|
||||||
_options.CleanSession = value;
|
_options.CleanSession = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean start is used in MQTT versions 5.0.0 and higher. It is the same as setting "CleanSession".
|
||||||
|
/// </summary>
|
||||||
|
public MqttClientOptionsBuilder WithCleanStart(bool value = true)
|
||||||
|
{
|
||||||
|
_options.CleanSession = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithClientId(string value)
|
public MqttClientOptionsBuilder WithClientId(string value)
|
||||||
{
|
{
|
||||||
_options.ClientId = value;
|
_options.ClientId = value;
|
||||||
@@ -188,6 +210,18 @@ namespace MQTTnet.Client
|
|||||||
return WithKeepAlivePeriod(TimeSpan.Zero);
|
return WithKeepAlivePeriod(TimeSpan.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Usually the MQTT packets can be send partially. This is done by using multiple TCP packets
|
||||||
|
/// or WebSocket frames etc. Unfortunately not all brokers (like Amazon Web Services (AWS)) do support this feature and
|
||||||
|
/// will close the connection when receiving such packets. If such a service is used this flag must
|
||||||
|
/// be set to _true_.
|
||||||
|
/// </summary>
|
||||||
|
public MqttClientOptionsBuilder WithoutPacketFragmentation()
|
||||||
|
{
|
||||||
|
_options.AllowPacketFragmentation = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
|
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
|
||||||
{
|
{
|
||||||
if (value == MqttProtocolVersion.Unknown)
|
if (value == MqttProtocolVersion.Unknown)
|
||||||
@@ -373,6 +407,18 @@ namespace MQTTnet.Client
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttClientOptionsBuilder WithWillContentType(string willContentType)
|
||||||
|
{
|
||||||
|
_options.WillContentType = willContentType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttClientOptionsBuilder WithWillCorrelationData(byte[] willCorrelationData)
|
||||||
|
{
|
||||||
|
_options.WillCorrelationData = willCorrelationData;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWillDelayInterval(uint willDelayInterval)
|
public MqttClientOptionsBuilder WithWillDelayInterval(uint willDelayInterval)
|
||||||
{
|
{
|
||||||
_options.WillDelayInterval = willDelayInterval;
|
_options.WillDelayInterval = willDelayInterval;
|
||||||
@@ -385,6 +431,18 @@ namespace MQTTnet.Client
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttClientOptionsBuilder WithWillPayload(ArraySegment<byte> willPayload)
|
||||||
|
{
|
||||||
|
if (willPayload.Count == 0)
|
||||||
|
{
|
||||||
|
_options.WillPayload = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_options.WillPayload = willPayload.ToArray();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWillPayload(string willPayload)
|
public MqttClientOptionsBuilder WithWillPayload(string willPayload)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(willPayload))
|
if (string.IsNullOrEmpty(willPayload))
|
||||||
@@ -396,45 +454,33 @@ namespace MQTTnet.Client
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttClientOptionsBuilder WithWillPayloadFormatIndicator(MqttPayloadFormatIndicator willPayloadFormatIndicator)
|
||||||
|
{
|
||||||
|
_options.WillPayloadFormatIndicator = willPayloadFormatIndicator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel willQualityOfServiceLevel)
|
public MqttClientOptionsBuilder WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel willQualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
_options.WillQualityOfServiceLevel = willQualityOfServiceLevel;
|
_options.WillQualityOfServiceLevel = willQualityOfServiceLevel;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWillTopic(string willTopic)
|
|
||||||
{
|
|
||||||
_options.WillTopic = willTopic;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWillRetain(bool willRetain = true)
|
|
||||||
{
|
|
||||||
_options.WillRetain = willRetain;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWillContentType(string willContentType)
|
|
||||||
{
|
|
||||||
_options.WillContentType = willContentType;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWillCorrelationData(byte[] willCorrelationData)
|
|
||||||
{
|
|
||||||
_options.WillCorrelationData = willCorrelationData;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWillResponseTopic(string willResponseTopic)
|
public MqttClientOptionsBuilder WithWillResponseTopic(string willResponseTopic)
|
||||||
{
|
{
|
||||||
_options.WillResponseTopic = willResponseTopic;
|
_options.WillResponseTopic = willResponseTopic;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWillPayloadFormatIndicator(MqttPayloadFormatIndicator willPayloadFormatIndicator)
|
public MqttClientOptionsBuilder WithWillRetain(bool willRetain = true)
|
||||||
{
|
{
|
||||||
_options.WillPayloadFormatIndicator = willPayloadFormatIndicator;
|
_options.WillRetain = willRetain;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MqttClientOptionsBuilder WithWillTopic(string willTopic)
|
||||||
|
{
|
||||||
|
_options.WillTopic = willTopic;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Security;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
@@ -30,11 +31,21 @@ namespace MQTTnet.Client
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if NETCOREAPP3_1_OR_GREATER
|
#if NETCOREAPP3_1_OR_GREATER
|
||||||
public List<System.Net.Security.SslApplicationProtocol> ApplicationProtocols { get; set; }
|
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
|
||||||
|
|
||||||
public System.Net.Security.CipherSuitesPolicy CipherSuitesPolicy { get; set; }
|
public CipherSuitesPolicy CipherSuitesPolicy { get; set; }
|
||||||
|
|
||||||
|
public EncryptionPolicy EncryptionPolicy { get; set; } = EncryptionPolicy.RequireEncryption;
|
||||||
|
|
||||||
|
public bool AllowRenegotiation { get; set; } = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the target host.
|
||||||
|
/// If the value is null or empty the same host as the TCP socket host will be used.
|
||||||
|
/// </summary>
|
||||||
|
public string TargetHost { get; set; }
|
||||||
|
|
||||||
#if NET48 || NETCOREAPP3_1_OR_GREATER
|
#if NET48 || NETCOREAPP3_1_OR_GREATER
|
||||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12 | SslProtocols.Tls13;
|
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12 | SslProtocols.Tls13;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
// 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.Net;
|
using System.Net;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
namespace MQTTnet.Client
|
||||||
{
|
{
|
||||||
@@ -11,6 +13,8 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
public CookieContainer CookieContainer { get; set; }
|
public CookieContainer CookieContainer { get; set; }
|
||||||
|
|
||||||
|
public ICredentials Credentials { get; set; }
|
||||||
|
|
||||||
public MqttClientWebSocketProxyOptions ProxyOptions { get; set; }
|
public MqttClientWebSocketProxyOptions ProxyOptions { get; set; }
|
||||||
|
|
||||||
public IDictionary<string, string> RequestHeaders { get; set; }
|
public IDictionary<string, string> RequestHeaders { get; set; }
|
||||||
@@ -25,5 +29,27 @@ namespace MQTTnet.Client
|
|||||||
{
|
{
|
||||||
return Uri;
|
return Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NETSTANDARD1_3
|
||||||
|
#if !WINDOWS_UWP
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the keep alive interval for the Web Socket connection.
|
||||||
|
/// This is not related to the keep alive interval for the MQTT protocol.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan KeepAliveInterval { get; set; } = WebSocket.DefaultKeepAliveInterval;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the default (system) credentials should be used when connecting via Web Socket connection.
|
||||||
|
/// This is not related to the credentials which are used for the MQTT protocol.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseDefaultCredentials { get; set; }
|
||||||
|
#else
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the keep alive interval for the Web Socket connection.
|
||||||
|
/// This is not related to the keep alive interval for the MQTT protocol.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan KeepAliveInterval { get; set; } = TimeSpan.FromSeconds(30);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,8 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
public bool BypassOnLocal { get; set; }
|
public bool BypassOnLocal { get; set; }
|
||||||
|
|
||||||
|
public bool UseDefaultCredentials { get; set; }
|
||||||
|
|
||||||
public string[] BypassList { get; set; }
|
public string[] BypassList { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ namespace MQTTnet.Client
|
|||||||
/// Gets or sets the reason code.
|
/// Gets or sets the reason code.
|
||||||
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MqttClientPublishReasonCode ReasonCode { get; } = MqttClientPublishReasonCode.Success;
|
public MqttClientPublishReasonCode ReasonCode { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the reason string.
|
/// Gets or sets the reason string.
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ namespace MQTTnet.Client
|
|||||||
|
|
||||||
internal MqttPublishPacket PublishPacket { get; set; }
|
internal MqttPublishPacket PublishPacket { get; set; }
|
||||||
|
|
||||||
public Task AcknowledgeAsync(CancellationToken cancellationToken)
|
public Task AcknowledgeAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (_acknowledgeHandler == null)
|
if (_acknowledgeHandler == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ namespace MQTTnet.Client
|
|||||||
/// Gets the user properties which were part of the UNSUBACK packet.
|
/// Gets the user properties which were part of the UNSUBACK packet.
|
||||||
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; }
|
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,8 @@ namespace MQTTnet.Diagnostics
|
|||||||
return "net461";
|
return "net461";
|
||||||
#elif NET472
|
#elif NET472
|
||||||
return "net472";
|
return "net472";
|
||||||
|
#elif NET48
|
||||||
|
return "net48";
|
||||||
#elif NETSTANDARD1_3
|
#elif NETSTANDARD1_3
|
||||||
return "netstandard1.3";
|
return "netstandard1.3";
|
||||||
#elif NETSTANDARD2_0
|
#elif NETSTANDARD2_0
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ namespace MQTTnet.Formatter
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class MqttBufferWriter
|
public sealed class MqttBufferWriter
|
||||||
{
|
{
|
||||||
public const uint VariableByteIntegerMaxValue = 268435455;
|
const uint VariableByteIntegerMaxValue = 268435455;
|
||||||
|
const int EncodedStringMaxLength = 65535;
|
||||||
|
|
||||||
readonly int _maxBufferSize;
|
readonly int _maxBufferSize;
|
||||||
|
|
||||||
@@ -112,25 +113,7 @@ namespace MQTTnet.Formatter
|
|||||||
WriteBinary(propertyWriter._buffer, 0, propertyWriter.Length);
|
WriteBinary(propertyWriter._buffer, 0, propertyWriter.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteBinary(byte[] buffer, int offset, int count)
|
public void WriteBinary(byte[] value)
|
||||||
{
|
|
||||||
if (buffer == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsureAdditionalCapacity(count);
|
|
||||||
|
|
||||||
MqttMemoryHelper.Copy(buffer, offset, _buffer, _position, count);
|
|
||||||
IncreasePosition(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteBinaryData(byte[] value)
|
|
||||||
{
|
{
|
||||||
if (value == null || value.Length == 0)
|
if (value == null || value.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -155,6 +138,24 @@ namespace MQTTnet.Formatter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void WriteBinary(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
if (buffer == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureAdditionalCapacity(count);
|
||||||
|
|
||||||
|
MqttMemoryHelper.Copy(buffer, offset, _buffer, _position, count);
|
||||||
|
IncreasePosition(count);
|
||||||
|
}
|
||||||
|
|
||||||
public void WriteByte(byte @byte)
|
public void WriteByte(byte @byte)
|
||||||
{
|
{
|
||||||
EnsureAdditionalCapacity(1);
|
EnsureAdditionalCapacity(1);
|
||||||
@@ -186,6 +187,13 @@ namespace MQTTnet.Formatter
|
|||||||
|
|
||||||
var writtenBytes = Encoding.UTF8.GetBytes(value, 0, value.Length, _buffer, _position + 2);
|
var writtenBytes = Encoding.UTF8.GetBytes(value, 0, value.Length, _buffer, _position + 2);
|
||||||
|
|
||||||
|
// From RFC: 1.5.4 UTF-8 Encoded String
|
||||||
|
// Unless stated otherwise all UTF-8 encoded strings can have any length in the range 0 to 65,535 bytes.
|
||||||
|
if (writtenBytes > EncodedStringMaxLength)
|
||||||
|
{
|
||||||
|
throw new MqttProtocolViolationException($"The maximum string length is 65535. The current string has a length of {writtenBytes}.");
|
||||||
|
}
|
||||||
|
|
||||||
_buffer[_position] = (byte)(writtenBytes >> 8);
|
_buffer[_position] = (byte)(writtenBytes >> 8);
|
||||||
_buffer[_position + 1] = (byte)writtenBytes;
|
_buffer[_position + 1] = (byte)writtenBytes;
|
||||||
|
|
||||||
@@ -273,8 +281,7 @@ namespace MQTTnet.Formatter
|
|||||||
newBufferLength *= 2;
|
newBufferLength *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Array.Resize will create a new array and copy the existing
|
// Array.Resize will create a new array and copy the existing data to the new one.
|
||||||
// data to the new one.
|
|
||||||
Array.Resize(ref _buffer, newBufferLength);
|
Array.Resize(ref _buffer, newBufferLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +292,8 @@ namespace MQTTnet.Formatter
|
|||||||
|
|
||||||
if (_position > Length)
|
if (_position > Length)
|
||||||
{
|
{
|
||||||
|
// Also extend the position because we reached the end of the
|
||||||
|
// pre allocated buffer.
|
||||||
Length = _position;
|
Length = _position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,54 +2,12 @@
|
|||||||
// 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.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Formatter
|
namespace MQTTnet.Formatter
|
||||||
{
|
{
|
||||||
public static class MqttConnectReasonCodeConverter
|
public static class MqttConnectReasonCodeConverter
|
||||||
{
|
{
|
||||||
public static MqttConnectReasonCode ToConnectReasonCode(MqttConnectReturnCode returnCode)
|
|
||||||
{
|
|
||||||
switch (returnCode)
|
|
||||||
{
|
|
||||||
case MqttConnectReturnCode.ConnectionAccepted:
|
|
||||||
{
|
|
||||||
return MqttConnectReasonCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion:
|
|
||||||
{
|
|
||||||
return MqttConnectReasonCode.UnsupportedProtocolVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword:
|
|
||||||
{
|
|
||||||
return MqttConnectReasonCode.BadUserNameOrPassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttConnectReturnCode.ConnectionRefusedIdentifierRejected:
|
|
||||||
{
|
|
||||||
return MqttConnectReasonCode.ClientIdentifierNotValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttConnectReturnCode.ConnectionRefusedServerUnavailable:
|
|
||||||
{
|
|
||||||
return MqttConnectReasonCode.ServerUnavailable;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttConnectReturnCode.ConnectionRefusedNotAuthorized:
|
|
||||||
{
|
|
||||||
return MqttConnectReasonCode.NotAuthorized;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3).");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MqttConnectReturnCode ToConnectReturnCode(MqttConnectReasonCode reasonCode)
|
public static MqttConnectReturnCode ToConnectReturnCode(MqttConnectReasonCode reasonCode)
|
||||||
{
|
{
|
||||||
switch (reasonCode)
|
switch (reasonCode)
|
||||||
@@ -59,11 +17,13 @@ namespace MQTTnet.Formatter
|
|||||||
return MqttConnectReturnCode.ConnectionAccepted;
|
return MqttConnectReturnCode.ConnectionAccepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MqttConnectReasonCode.Banned:
|
||||||
case MqttConnectReasonCode.NotAuthorized:
|
case MqttConnectReasonCode.NotAuthorized:
|
||||||
{
|
{
|
||||||
return MqttConnectReturnCode.ConnectionRefusedNotAuthorized;
|
return MqttConnectReturnCode.ConnectionRefusedNotAuthorized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MqttConnectReasonCode.BadAuthenticationMethod:
|
||||||
case MqttConnectReasonCode.BadUserNameOrPassword:
|
case MqttConnectReasonCode.BadUserNameOrPassword:
|
||||||
{
|
{
|
||||||
return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
|
return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
|
||||||
@@ -79,6 +39,7 @@ namespace MQTTnet.Formatter
|
|||||||
return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion;
|
return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MqttConnectReasonCode.UseAnotherServer:
|
||||||
case MqttConnectReasonCode.ServerUnavailable:
|
case MqttConnectReasonCode.ServerUnavailable:
|
||||||
case MqttConnectReasonCode.ServerBusy:
|
case MqttConnectReasonCode.ServerBusy:
|
||||||
case MqttConnectReasonCode.ServerMoved:
|
case MqttConnectReasonCode.ServerMoved:
|
||||||
@@ -87,9 +48,7 @@ namespace MQTTnet.Formatter
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion;
|
||||||
throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3).");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,14 @@ namespace MQTTnet.Formatter
|
|||||||
|
|
||||||
public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown;
|
public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown;
|
||||||
|
|
||||||
|
public void Cleanup()
|
||||||
|
{
|
||||||
|
_bufferWriter.Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
public MqttPacket Decode(ReceivedMqttPacket receivedMqttPacket)
|
public MqttPacket Decode(ReceivedMqttPacket receivedMqttPacket)
|
||||||
{
|
{
|
||||||
ThrowIfFormatterNotSet();
|
ThrowIfFormatterNotSet();
|
||||||
|
|
||||||
return _formatter.Decode(receivedMqttPacket);
|
return _formatter.Decode(receivedMqttPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,11 +54,6 @@ namespace MQTTnet.Formatter
|
|||||||
return _formatter.Encode(packet);
|
return _formatter.Encode(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cleanup()
|
|
||||||
{
|
|
||||||
_bufferWriter.Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IMqttPacketFormatter GetMqttPacketFormatter(MqttProtocolVersion protocolVersion, MqttBufferWriter bufferWriter)
|
public static IMqttPacketFormatter GetMqttPacketFormatter(MqttProtocolVersion protocolVersion, MqttBufferWriter bufferWriter)
|
||||||
{
|
{
|
||||||
if (protocolVersion == MqttProtocolVersion.Unknown)
|
if (protocolVersion == MqttProtocolVersion.Unknown)
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -463,7 +459,7 @@ namespace MQTTnet.Formatter.V3
|
|||||||
if (packet.WillFlag)
|
if (packet.WillFlag)
|
||||||
{
|
{
|
||||||
bufferWriter.WriteString(packet.WillTopic);
|
bufferWriter.WriteString(packet.WillTopic);
|
||||||
bufferWriter.WriteBinaryData(packet.WillMessage);
|
bufferWriter.WriteBinary(packet.WillMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet.Username != null)
|
if (packet.Username != null)
|
||||||
@@ -473,7 +469,7 @@ namespace MQTTnet.Formatter.V3
|
|||||||
|
|
||||||
if (packet.Password != null)
|
if (packet.Password != null)
|
||||||
{
|
{
|
||||||
bufferWriter.WriteBinaryData(packet.Password);
|
bufferWriter.WriteBinary(packet.Password);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||||
@@ -525,7 +521,7 @@ namespace MQTTnet.Formatter.V3
|
|||||||
if (packet.WillFlag)
|
if (packet.WillFlag)
|
||||||
{
|
{
|
||||||
bufferWriter.WriteString(packet.WillTopic);
|
bufferWriter.WriteString(packet.WillTopic);
|
||||||
bufferWriter.WriteBinaryData(packet.WillMessage);
|
bufferWriter.WriteBinary(packet.WillMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet.Username != null)
|
if (packet.Username != null)
|
||||||
@@ -535,7 +531,7 @@ namespace MQTTnet.Formatter.V3
|
|||||||
|
|
||||||
if (packet.Password != null)
|
if (packet.Password != null)
|
||||||
{
|
{
|
||||||
bufferWriter.WriteBinaryData(packet.Password);
|
bufferWriter.WriteBinary(packet.Password);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||||
|
|||||||
@@ -130,6 +130,9 @@ namespace MQTTnet.Formatter.V5
|
|||||||
MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce
|
MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Also set the return code of MQTT 3.1.1 for backward compatibility and debugging purposes.
|
||||||
|
packet.ReturnCode = MqttConnectReasonCodeConverter.ToConnectReturnCode(packet.ReasonCode);
|
||||||
|
|
||||||
var propertiesReader = new MqttV5PropertiesReader(_bufferReader);
|
var propertiesReader = new MqttV5PropertiesReader(_bufferReader);
|
||||||
while (propertiesReader.MoveNext())
|
while (propertiesReader.MoveNext())
|
||||||
{
|
{
|
||||||
@@ -528,7 +531,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)
|
||||||
@@ -189,7 +186,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
_propertiesWriter.Reset();
|
_propertiesWriter.Reset();
|
||||||
|
|
||||||
_bufferWriter.WriteString(packet.WillTopic);
|
_bufferWriter.WriteString(packet.WillTopic);
|
||||||
_bufferWriter.WriteBinaryData(packet.WillMessage);
|
_bufferWriter.WriteBinary(packet.WillMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet.Username != null)
|
if (packet.Username != null)
|
||||||
@@ -199,7 +196,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
|
|
||||||
if (packet.Password != null)
|
if (packet.Password != null)
|
||||||
{
|
{
|
||||||
_bufferWriter.WriteBinaryData(packet.Password);
|
_bufferWriter.WriteBinary(packet.Password);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ namespace MQTTnet.Formatter.V5
|
|||||||
}
|
}
|
||||||
|
|
||||||
_bufferWriter.WriteByte((byte)id);
|
_bufferWriter.WriteByte((byte)id);
|
||||||
_bufferWriter.WriteBinaryData(value);
|
_bufferWriter.WriteBinary(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteAsFourByteInteger(MqttPropertyId id, uint value)
|
void WriteAsFourByteInteger(MqttPropertyId id, uint value)
|
||||||
|
|||||||
@@ -40,7 +40,12 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
var bufferWriter = new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax);
|
var bufferWriter = new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax);
|
||||||
var packetFormatterAdapter = new MqttPacketFormatterAdapter(options.ProtocolVersion, bufferWriter);
|
var packetFormatterAdapter = new MqttPacketFormatterAdapter(options.ProtocolVersion, bufferWriter);
|
||||||
return new MqttChannelAdapter(channel, packetFormatterAdapter, packetInspector, logger);
|
|
||||||
|
return new MqttChannelAdapter(channel, packetFormatterAdapter, logger)
|
||||||
|
{
|
||||||
|
AllowPacketFragmentation = options.AllowPacketFragmentation,
|
||||||
|
PacketInspector = packetInspector
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,12 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
if (_tcpOptions.TlsOptions?.UseTls == true)
|
if (_tcpOptions.TlsOptions?.UseTls == true)
|
||||||
{
|
{
|
||||||
|
var targetHost = _tcpOptions.TlsOptions.TargetHost;
|
||||||
|
if (string.IsNullOrEmpty(targetHost))
|
||||||
|
{
|
||||||
|
targetHost = _tcpOptions.Server;
|
||||||
|
}
|
||||||
|
|
||||||
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -111,14 +117,16 @@ namespace MQTTnet.Implementations
|
|||||||
EnabledSslProtocols = _tcpOptions.TlsOptions.SslProtocol,
|
EnabledSslProtocols = _tcpOptions.TlsOptions.SslProtocol,
|
||||||
CertificateRevocationCheckMode =
|
CertificateRevocationCheckMode =
|
||||||
_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : _tcpOptions.TlsOptions.RevocationMode,
|
_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : _tcpOptions.TlsOptions.RevocationMode,
|
||||||
TargetHost = _tcpOptions.Server,
|
TargetHost = targetHost,
|
||||||
CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy
|
CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy,
|
||||||
|
EncryptionPolicy = _tcpOptions.TlsOptions.EncryptionPolicy,
|
||||||
|
AllowRenegotiation = _tcpOptions.TlsOptions.AllowRenegotiation
|
||||||
};
|
};
|
||||||
|
|
||||||
await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
|
await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
|
||||||
#else
|
#else
|
||||||
await sslStream.AuthenticateAsClientAsync(
|
await sslStream.AuthenticateAsClientAsync(
|
||||||
_tcpOptions.Server,
|
targetHost,
|
||||||
LoadCertificates(),
|
LoadCertificates(),
|
||||||
_tcpOptions.TlsOptions.SslProtocol,
|
_tcpOptions.TlsOptions.SslProtocol,
|
||||||
!_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors)
|
!_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors)
|
||||||
@@ -290,12 +298,12 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
X509CertificateCollection LoadCertificates()
|
X509CertificateCollection LoadCertificates()
|
||||||
{
|
{
|
||||||
var certificates = new X509CertificateCollection();
|
|
||||||
if (_tcpOptions.TlsOptions.Certificates == null)
|
if (_tcpOptions.TlsOptions.Certificates == null)
|
||||||
{
|
{
|
||||||
return certificates;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var certificates = new X509CertificateCollection();
|
||||||
foreach (var certificate in _tcpOptions.TlsOptions.Certificates)
|
foreach (var certificate in _tcpOptions.TlsOptions.Certificates)
|
||||||
{
|
{
|
||||||
certificates.Add(certificate);
|
certificates.Add(certificate);
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace MQTTnet.Implementations
|
|||||||
var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter);
|
var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter);
|
||||||
var tcpChannel = new MqttTcpChannel(args.Socket, clientCertificate, _options);
|
var tcpChannel = new MqttTcpChannel(args.Socket, clientCertificate, _options);
|
||||||
|
|
||||||
using (var clientAdapter = new MqttChannelAdapter(tcpChannel, packetFormatterAdapter, null, _rootLogger))
|
using (var clientAdapter = new MqttChannelAdapter(tcpChannel, packetFormatterAdapter, _rootLogger))
|
||||||
{
|
{
|
||||||
await clientHandler(clientAdapter).ConfigureAwait(false);
|
await clientHandler(clientAdapter).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,8 +229,9 @@ namespace MQTTnet.Implementations
|
|||||||
var bufferWriter = new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax);
|
var bufferWriter = new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax);
|
||||||
var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter);
|
var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter);
|
||||||
|
|
||||||
using (var clientAdapter = new MqttChannelAdapter(tcpChannel, packetFormatterAdapter, null, _rootLogger))
|
using (var clientAdapter = new MqttChannelAdapter(tcpChannel, packetFormatterAdapter, _rootLogger))
|
||||||
{
|
{
|
||||||
|
clientAdapter.AllowPacketFragmentation = _options.AllowPacketFragmentation;
|
||||||
await clientHandler(clientAdapter).ConfigureAwait(false);
|
await clientHandler(clientAdapter).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,6 +256,7 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once MethodHasAsyncOverload
|
||||||
stream?.Dispose();
|
stream?.Dispose();
|
||||||
clientSocket?.Dispose();
|
clientSocket?.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
await clientWebSocket.ConnectAsync(new Uri(uri), cancellationToken).ConfigureAwait(false);
|
await clientWebSocket.ConnectAsync(new Uri(uri), cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch
|
||||||
{
|
{
|
||||||
// Prevent a memory leak when always creating new instance which will fail while connecting.
|
// Prevent a memory leak when always creating new instance which will fail while connecting.
|
||||||
clientWebSocket.Dispose();
|
clientWebSocket.Dispose();
|
||||||
@@ -155,14 +155,26 @@ namespace MQTTnet.Implementations
|
|||||||
throw new NotSupportedException("Proxies are not supported when using '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);
|
||||||
|
WebProxy webProxy;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_options.ProxyOptions.Username) && !string.IsNullOrEmpty(_options.ProxyOptions.Password))
|
if (!string.IsNullOrEmpty(_options.ProxyOptions.Username) && !string.IsNullOrEmpty(_options.ProxyOptions.Password))
|
||||||
{
|
{
|
||||||
var credentials = new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain);
|
var credentials = new NetworkCredential(_options.ProxyOptions.Username, _options.ProxyOptions.Password, _options.ProxyOptions.Domain);
|
||||||
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList, credentials);
|
webProxy = new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList, credentials);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
webProxy = new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WebProxy(proxyUri, _options.ProxyOptions.BypassOnLocal, _options.ProxyOptions.BypassList);
|
if (_options.ProxyOptions.UseDefaultCredentials)
|
||||||
|
{
|
||||||
|
// Only update the property if required because setting it to false will alter
|
||||||
|
// the used credentials internally!
|
||||||
|
webProxy.UseDefaultCredentials = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return webProxy;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +219,26 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NETSTANDARD1_3
|
||||||
|
#if !WINDOWS_UWP
|
||||||
|
// Only set the value if it is actually true. This property is not supported on all platforms
|
||||||
|
// and will throw a _PlatformNotSupported_ (i.e. WASM) exception when being used regardless of the actual value.
|
||||||
|
if (_options.UseDefaultCredentials)
|
||||||
|
{
|
||||||
|
clientWebSocket.Options.UseDefaultCredentials = _options.UseDefaultCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_options.KeepAliveInterval != WebSocket.DefaultKeepAliveInterval)
|
||||||
|
{
|
||||||
|
clientWebSocket.Options.KeepAliveInterval = _options.KeepAliveInterval;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
if (_options.Credentials != null)
|
||||||
|
{
|
||||||
|
clientWebSocket.Options.Credentials = _options.Credentials;
|
||||||
|
}
|
||||||
|
|
||||||
var certificateValidationHandler = _options.TlsOptions?.CertificateValidationHandler;
|
var certificateValidationHandler = _options.TlsOptions?.CertificateValidationHandler;
|
||||||
if (certificateValidationHandler != null)
|
if (certificateValidationHandler != null)
|
||||||
{
|
{
|
||||||
@@ -220,6 +252,8 @@ namespace MQTTnet.Implementations
|
|||||||
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net452'.");
|
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net452'.");
|
||||||
#elif NET461
|
#elif NET461
|
||||||
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net461'.");
|
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net461'.");
|
||||||
|
#elif NET48
|
||||||
|
throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net48'.");
|
||||||
#else
|
#else
|
||||||
clientWebSocket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
clientWebSocket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// 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.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
|
|
||||||
namespace MQTTnet.Internal
|
|
||||||
{
|
|
||||||
public static class MqttTaskTimeout
|
|
||||||
{
|
|
||||||
public static async Task WaitAsync(Func<CancellationToken, Task> action, TimeSpan timeout, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
|
||||||
|
|
||||||
using (var timeoutCts = new CancellationTokenSource(timeout))
|
|
||||||
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await action(linkedCts.Token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException exception)
|
|
||||||
{
|
|
||||||
var timeoutReached = timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested;
|
|
||||||
if (timeoutReached)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationTimedOutException(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ namespace MQTTnet.LowLevelClient
|
|||||||
{
|
{
|
||||||
public interface ILowLevelMqttClient : IDisposable
|
public interface ILowLevelMqttClient : IDisposable
|
||||||
{
|
{
|
||||||
event Func<InspectMqttPacketEventArgs, Task> InspectPackage;
|
event Func<InspectMqttPacketEventArgs, Task> InspectPacketAsync;
|
||||||
|
|
||||||
bool IsConnected { get; }
|
bool IsConnected { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace MQTTnet.LowLevelClient
|
|||||||
_logger = logger.WithSource(nameof(LowLevelMqttClient));
|
_logger = logger.WithSource(nameof(LowLevelMqttClient));
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Func<InspectMqttPacketEventArgs, Task> InspectPackage
|
public event Func<InspectMqttPacketEventArgs, Task> InspectPacketAsync
|
||||||
{
|
{
|
||||||
add => _inspectPacketEvent.AddHandler(value);
|
add => _inspectPacketEvent.AddHandler(value);
|
||||||
remove => _inspectPacketEvent.RemoveHandler(value);
|
remove => _inspectPacketEvent.RemoveHandler(value);
|
||||||
@@ -52,13 +52,19 @@ namespace MQTTnet.LowLevelClient
|
|||||||
throw new InvalidOperationException("Low level MQTT client is already connected. Disconnect first before connecting again.");
|
throw new InvalidOperationException("Low level MQTT client is already connected. Disconnect first before connecting again.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var newAdapter = _clientAdapterFactory.CreateClientAdapter(options, new MqttPacketInspector(_inspectPacketEvent, _rootLogger), _rootLogger);
|
MqttPacketInspector packetInspector = null;
|
||||||
|
if (_inspectPacketEvent.HasHandlers)
|
||||||
|
{
|
||||||
|
packetInspector = new MqttPacketInspector(_inspectPacketEvent, _rootLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newAdapter = _clientAdapterFactory.CreateClientAdapter(options, packetInspector, _rootLogger);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.Verbose("Trying to connect with server '{0}'.", options.ChannelOptions);
|
_logger.Verbose("Trying to connect with server '{0}'", options.ChannelOptions);
|
||||||
await newAdapter.ConnectAsync(cancellationToken).ConfigureAwait(false);
|
await newAdapter.ConnectAsync(cancellationToken).ConfigureAwait(false);
|
||||||
_logger.Verbose("Connection with server established.");
|
_logger.Verbose("Connection with server established");
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||||
<LangVersion>7.3</LangVersion>
|
<LangVersion>7.3</LangVersion>
|
||||||
<AssemblyName>MQTTnet</AssemblyName>
|
<AssemblyName>MQTTnet</AssemblyName>
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -2,11 +2,14 @@
|
|||||||
// 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.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.Exceptions;
|
||||||
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
@@ -17,8 +20,9 @@ namespace MQTTnet
|
|||||||
string _contentType;
|
string _contentType;
|
||||||
byte[] _correlationData;
|
byte[] _correlationData;
|
||||||
uint _messageExpiryInterval;
|
uint _messageExpiryInterval;
|
||||||
byte[] _payload;
|
|
||||||
MqttPayloadFormatIndicator _payloadFormatIndicator;
|
MqttPayloadFormatIndicator _payloadFormatIndicator;
|
||||||
|
ArraySegment<byte> _payloadSegment;
|
||||||
MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
||||||
string _responseTopic;
|
string _responseTopic;
|
||||||
bool _retain;
|
bool _retain;
|
||||||
@@ -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,13 @@ 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 WithPayload(ArraySegment<byte> payloadSegment)
|
||||||
|
{
|
||||||
|
_payloadSegment = payloadSegment;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,68 +103,59 @@ 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[])) : WithPayload(payload, payload.Length - payload.Position);
|
||||||
{
|
|
||||||
_payload = null;
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -167,6 +168,20 @@ namespace MQTTnet
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttApplicationMessageBuilder WithPayloadSegment(ArraySegment<byte> payloadSegment)
|
||||||
|
{
|
||||||
|
_payloadSegment = payloadSegment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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>
|
||||||
/// The quality of service level.
|
/// 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
|
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet
|
namespace MQTTnet
|
||||||
{
|
{
|
||||||
@@ -11,19 +12,23 @@ 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)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
}
|
||||||
|
|
||||||
if (applicationMessage.Payload == null)
|
if(applicationMessage.PayloadSegment == EmptyBuffer.ArraySegment)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (applicationMessage.Payload.Length == 0)
|
if (applicationMessage.PayloadSegment.Array == null)
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(applicationMessage.Payload, 0, applicationMessage.Payload.Length);
|
var payloadSegment = applicationMessage.PayloadSegment;
|
||||||
|
return Encoding.UTF8.GetString(payloadSegment.Array, payloadSegment.Offset, payloadSegment.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace MQTTnet.Packets
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"Disconnect: [ReasonCode={ReasonCode}]";
|
return $"Disconnect: [ReasonCode={ReasonCode}] [ReasonString={ReasonString}] [ServerReference={ServerReference}] [SessionExpiryInterval={SessionExpiryInterval}]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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}]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,26 +4,33 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
|
using MQTTnet.Packets;
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public sealed class ClientConnectedEventArgs : EventArgs
|
public sealed class ClientConnectedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public ClientConnectedEventArgs(string clientId, string userName, MqttProtocolVersion protocolVersion, string endpoint, IDictionary sessionItems)
|
readonly MqttConnectPacket _connectPacket;
|
||||||
|
|
||||||
|
public ClientConnectedEventArgs(MqttConnectPacket connectPacket, MqttProtocolVersion protocolVersion, string endpoint, IDictionary sessionItems)
|
||||||
{
|
{
|
||||||
ClientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
|
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
|
||||||
UserName = userName;
|
|
||||||
ProtocolVersion = protocolVersion;
|
ProtocolVersion = protocolVersion;
|
||||||
Endpoint = endpoint;
|
Endpoint = endpoint;
|
||||||
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
|
SessionItems = sessionItems ?? throw new ArgumentNullException(nameof(sessionItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] AuthenticationData => _connectPacket.AuthenticationData;
|
||||||
|
|
||||||
|
public string AuthenticationMethod => _connectPacket.AuthenticationMethod;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the client identifier of the connected client.
|
/// Gets the client identifier of the connected client.
|
||||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ClientId { get; }
|
public string ClientId => _connectPacket.ClientId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the endpoint of the connected client.
|
/// Gets the endpoint of the connected client.
|
||||||
@@ -43,6 +50,12 @@ namespace MQTTnet.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user name of the connected client.
|
/// Gets the user name of the connected client.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string UserName { get; }
|
public string UserName => _connectPacket.Username;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the user properties sent by the client.
|
||||||
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
|
/// </summary>
|
||||||
|
public List<MqttUserProperty> UserProperties => _connectPacket?.UserProperties;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server
|
||||||
|
{
|
||||||
|
public sealed class InterceptingClientApplicationMessageEnqueueEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public InterceptingClientApplicationMessageEnqueueEventArgs(string senderClientId, string receiverClientId, MqttApplicationMessage applicationMessage)
|
||||||
|
{
|
||||||
|
SenderClientId = senderClientId ?? throw new ArgumentNullException(nameof(senderClientId));
|
||||||
|
ReceiverClientId = receiverClientId ?? throw new ArgumentNullException(nameof(receiverClientId));
|
||||||
|
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the enqueue of the application message should be performed or not.
|
||||||
|
/// If set to _False_ the client will not receive the application message.
|
||||||
|
/// </summary>
|
||||||
|
public bool AcceptEnqueue { get; set; } = true;
|
||||||
|
|
||||||
|
public MqttApplicationMessage ApplicationMessage { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates if the connection with the sender should be closed.
|
||||||
|
/// </summary>
|
||||||
|
public bool CloseSenderConnection { get; set; }
|
||||||
|
|
||||||
|
public string ReceiverClientId { get; }
|
||||||
|
|
||||||
|
public string SenderClientId { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,12 +16,14 @@ namespace MQTTnet.Server
|
|||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
string clientId,
|
string clientId,
|
||||||
MqttSessionStatus session,
|
MqttSessionStatus session,
|
||||||
MqttTopicFilter topicFilter)
|
MqttTopicFilter topicFilter,
|
||||||
|
List<MqttUserProperty> userProperties)
|
||||||
{
|
{
|
||||||
CancellationToken = cancellationToken;
|
CancellationToken = cancellationToken;
|
||||||
ClientId = clientId;
|
ClientId = clientId;
|
||||||
Session = session;
|
Session = session;
|
||||||
TopicFilter = topicFilter;
|
TopicFilter = topicFilter;
|
||||||
|
UserProperties = userProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -75,6 +77,7 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the user properties.
|
/// Gets or sets the user properties.
|
||||||
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<MqttUserProperty> UserProperties { get; set; }
|
public List<MqttUserProperty> UserProperties { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,21 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using MQTTnet.Packets;
|
||||||
|
|
||||||
namespace MQTTnet.Server
|
namespace MQTTnet.Server
|
||||||
{
|
{
|
||||||
public sealed class InterceptingUnsubscriptionEventArgs : EventArgs
|
public sealed class InterceptingUnsubscriptionEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public InterceptingUnsubscriptionEventArgs(CancellationToken cancellationToken, string clientId, IDictionary sessionItems, string topic)
|
public InterceptingUnsubscriptionEventArgs(CancellationToken cancellationToken, string clientId, IDictionary sessionItems, string topic, List<MqttUserProperty> userProperties)
|
||||||
{
|
{
|
||||||
CancellationToken = cancellationToken;
|
CancellationToken = cancellationToken;
|
||||||
ClientId = clientId;
|
ClientId = clientId;
|
||||||
SessionItems = sessionItems;
|
SessionItems = sessionItems;
|
||||||
Topic = topic;
|
Topic = topic;
|
||||||
|
UserProperties = userProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -59,5 +62,11 @@ namespace MQTTnet.Server
|
|||||||
/// separator).
|
/// separator).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Topic { get; }
|
public string Topic { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the user properties.
|
||||||
|
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||||
|
/// </summary>
|
||||||
|
public List<MqttUserProperty> UserProperties { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,17 +94,18 @@ namespace MQTTnet.Server
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var cancellationToken = _cancellationToken.Token;
|
var cancellationToken = _cancellationToken.Token;
|
||||||
|
IsRunning = true;
|
||||||
|
|
||||||
_ = Task.Factory.StartNew(() => SendPacketsLoop(cancellationToken), cancellationToken, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false);
|
_ = Task.Factory.StartNew(() => SendPacketsLoop(cancellationToken), cancellationToken, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false);
|
||||||
|
|
||||||
IsRunning = true;
|
|
||||||
|
|
||||||
await ReceivePackagesLoop(cancellationToken).ConfigureAwait(false);
|
await ReceivePackagesLoop(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
|
|
||||||
|
Session.DisconnectedTimestamp = DateTime.UtcNow;
|
||||||
|
|
||||||
_cancellationToken?.TryCancel();
|
_cancellationToken?.TryCancel();
|
||||||
_cancellationToken?.Dispose();
|
_cancellationToken?.Dispose();
|
||||||
_cancellationToken = null;
|
_cancellationToken = null;
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
// The _sessions dictionary contains all session, the _subscriberSessions hash set contains subscriber sessions only.
|
// The _sessions dictionary contains all session, the _subscriberSessions hash set contains subscriber sessions only.
|
||||||
// See the MqttSubscription object for a detailed explanation.
|
// See the MqttSubscription object for a detailed explanation.
|
||||||
readonly Dictionary<string, MqttSession> _sessions = new Dictionary<string, MqttSession>(4096);
|
readonly MqttSessionsStorage _sessionsStorage = new MqttSessionsStorage();
|
||||||
|
|
||||||
readonly object _sessionsManagementLock = new object();
|
readonly ReaderWriterLockSlim _sessionsManagementLock = new ReaderWriterLockSlim();
|
||||||
readonly HashSet<MqttSession> _subscriberSessions = new HashSet<MqttSession>();
|
readonly HashSet<MqttSession> _subscriberSessions = new HashSet<MqttSession>();
|
||||||
|
|
||||||
public MqttClientSessionsManager(
|
public MqttClientSessionsManager(
|
||||||
@@ -58,7 +58,7 @@ namespace MQTTnet.Server
|
|||||||
_eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
|
_eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CloseAllConnectionsAsync()
|
public async Task CloseAllConnections()
|
||||||
{
|
{
|
||||||
List<MqttClient> connections;
|
List<MqttClient> connections;
|
||||||
lock (_clients)
|
lock (_clients)
|
||||||
@@ -78,24 +78,24 @@ namespace MQTTnet.Server
|
|||||||
_logger.Verbose("Deleting session for client '{0}'.", clientId);
|
_logger.Verbose("Deleting session for client '{0}'.", clientId);
|
||||||
|
|
||||||
MqttClient connection;
|
MqttClient connection;
|
||||||
|
|
||||||
lock (_clients)
|
lock (_clients)
|
||||||
{
|
{
|
||||||
_clients.TryGetValue(clientId, out connection);
|
_clients.TryGetValue(clientId, out connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttSession session;
|
MqttSession session;
|
||||||
|
_sessionsManagementLock.EnterWriteLock();
|
||||||
lock (_sessionsManagementLock)
|
try
|
||||||
{
|
{
|
||||||
_sessions.TryGetValue(clientId, out session);
|
if (_sessionsStorage.TryRemoveSession(clientId, out session))
|
||||||
_sessions.Remove(clientId);
|
|
||||||
|
|
||||||
if (session != null)
|
|
||||||
{
|
{
|
||||||
_subscriberSessions.Remove(session);
|
_subscriberSessions.Remove(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sessionsManagementLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -106,7 +106,7 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
_logger.Error(exception, $"Error while deleting session '{clientId}'.");
|
_logger.Error(exception, "Error while deleting session '{0}'", clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -119,12 +119,12 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
_logger.Error(exception, $"Error while executing session deleted event for session '{clientId}'.");
|
_logger.Error(exception, "Error while executing session deleted event for session '{0}'", clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
session?.Dispose();
|
session?.Dispose();
|
||||||
|
|
||||||
_logger.Verbose("Session for client '{0}' deleted.", clientId);
|
_logger.Verbose("Session of client '{0}' deleted", clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DispatchApplicationMessageResult> DispatchApplicationMessage(
|
public async Task<DispatchApplicationMessageResult> DispatchApplicationMessage(
|
||||||
@@ -173,13 +173,18 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<MqttSession> subscriberSessions;
|
List<MqttSession> subscriberSessions;
|
||||||
lock (_sessionsManagementLock)
|
_sessionsManagementLock.EnterReadLock();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
subscriberSessions = _subscriberSessions.ToList();
|
subscriberSessions = _subscriberSessions.ToList();
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sessionsManagementLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate application message topic hash once for subscription checks
|
// Calculate application message topic hash once for subscription checks
|
||||||
MqttSubscription.CalculateTopicHash(applicationMessage.Topic, out var topicHash, out _, out _);
|
MqttTopicHash.Calculate(applicationMessage.Topic, out var topicHash, out _, out _);
|
||||||
|
|
||||||
foreach (var session in subscriberSessions)
|
foreach (var session in subscriberSessions)
|
||||||
{
|
{
|
||||||
@@ -200,6 +205,18 @@ namespace MQTTnet.Server
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_eventContainer.InterceptingClientEnqueueEvent.HasHandlers)
|
||||||
|
{
|
||||||
|
var eventArgs = new InterceptingClientApplicationMessageEnqueueEventArgs(senderId, session.Id, applicationMessage);
|
||||||
|
await _eventContainer.InterceptingClientEnqueueEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!eventArgs.AcceptEnqueue)
|
||||||
|
{
|
||||||
|
// Continue checking the other subscriptions
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var publishPacketCopy = MqttPacketFactories.Publish.Create(applicationMessage);
|
var publishPacketCopy = MqttPacketFactories.Publish.Create(applicationMessage);
|
||||||
publishPacketCopy.QualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel;
|
publishPacketCopy.QualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel;
|
||||||
publishPacketCopy.SubscriptionIdentifiers = checkSubscriptionsResult.SubscriptionIdentifiers;
|
publishPacketCopy.SubscriptionIdentifiers = checkSubscriptionsResult.SubscriptionIdentifiers;
|
||||||
@@ -219,10 +236,10 @@ namespace MQTTnet.Server
|
|||||||
publishPacketCopy.Retain = false;
|
publishPacketCopy.Retain = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
session.EnqueueDataPacket(new MqttPacketBusItem(publishPacketCopy));
|
|
||||||
matchingSubscribersCount++;
|
matchingSubscribersCount++;
|
||||||
|
|
||||||
_logger.Verbose("Client '{0}': Queued PUBLISH packet with topic '{1}'.", session.Id, applicationMessage.Topic);
|
session.EnqueueDataPacket(new MqttPacketBusItem(publishPacketCopy));
|
||||||
|
_logger.Verbose("Client '{0}': Queued PUBLISH packet with topic '{1}'", session.Id, applicationMessage.Topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchingSubscribersCount == 0)
|
if (matchingSubscribersCount == 0)
|
||||||
@@ -233,7 +250,7 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
_logger.Error(exception, "Unhandled exception while processing next queued application message.");
|
_logger.Error(exception, "Error while processing next queued application message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,13 +261,17 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
_createConnectionSyncRoot.Dispose();
|
_createConnectionSyncRoot.Dispose();
|
||||||
|
|
||||||
lock (_sessionsManagementLock)
|
_sessionsManagementLock.EnterWriteLock();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
foreach (var sessionItem in _sessions)
|
_sessionsStorage.Dispose();
|
||||||
{
|
|
||||||
sessionItem.Value.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sessionsManagementLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sessionsManagementLock?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttClient GetClient(string id)
|
public MqttClient GetClient(string id)
|
||||||
@@ -274,17 +295,17 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IList<MqttClientStatus>> GetClientStatusesAsync()
|
public Task<IList<MqttClientStatus>> GetClientsStatus()
|
||||||
{
|
{
|
||||||
var result = new List<MqttClientStatus>();
|
var result = new List<MqttClientStatus>();
|
||||||
|
|
||||||
lock (_clients)
|
lock (_clients)
|
||||||
{
|
{
|
||||||
foreach (var connection in _clients.Values)
|
foreach (var client in _clients.Values)
|
||||||
{
|
{
|
||||||
var clientStatus = new MqttClientStatus(connection)
|
var clientStatus = new MqttClientStatus(client)
|
||||||
{
|
{
|
||||||
Session = new MqttSessionStatus(connection.Session)
|
Session = new MqttSessionStatus(client.Session)
|
||||||
};
|
};
|
||||||
|
|
||||||
result.Add(clientStatus);
|
result.Add(clientStatus);
|
||||||
@@ -294,18 +315,23 @@ namespace MQTTnet.Server
|
|||||||
return Task.FromResult((IList<MqttClientStatus>)result);
|
return Task.FromResult((IList<MqttClientStatus>)result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IList<MqttSessionStatus>> GetSessionStatusAsync()
|
public Task<IList<MqttSessionStatus>> GetSessionsStatus()
|
||||||
{
|
{
|
||||||
var result = new List<MqttSessionStatus>();
|
var result = new List<MqttSessionStatus>();
|
||||||
|
|
||||||
lock (_sessionsManagementLock)
|
_sessionsManagementLock.EnterReadLock();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
foreach (var sessionItem in _sessions)
|
foreach (var session in _sessionsStorage.ReadAllSessions())
|
||||||
{
|
{
|
||||||
var sessionStatus = new MqttSessionStatus(sessionItem.Value);
|
var sessionStatus = new MqttSessionStatus(session);
|
||||||
result.Add(sessionStatus);
|
result.Add(sessionStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sessionsManagementLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
return Task.FromResult((IList<MqttSessionStatus>)result);
|
return Task.FromResult((IList<MqttSessionStatus>)result);
|
||||||
}
|
}
|
||||||
@@ -340,9 +366,7 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
if (_eventContainer.ClientConnectedEvent.HasHandlers)
|
if (_eventContainer.ClientConnectedEvent.HasHandlers)
|
||||||
{
|
{
|
||||||
var eventArgs = new ClientConnectedEventArgs(
|
var eventArgs = new ClientConnectedEventArgs(connectPacket,
|
||||||
connectPacket.ClientId,
|
|
||||||
connectPacket.Username,
|
|
||||||
channelAdapter.PacketFormatterAdapter.ProtocolVersion,
|
channelAdapter.PacketFormatterAdapter.ProtocolVersion,
|
||||||
channelAdapter.Endpoint,
|
channelAdapter.Endpoint,
|
||||||
client.Session.Items);
|
client.Session.Items);
|
||||||
@@ -403,7 +427,8 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public void OnSubscriptionsAdded(MqttSession clientSession, List<string> topics)
|
public void OnSubscriptionsAdded(MqttSession clientSession, List<string> topics)
|
||||||
{
|
{
|
||||||
lock (_sessionsManagementLock)
|
_sessionsManagementLock.EnterWriteLock();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (!clientSession.HasSubscribedTopics)
|
if (!clientSession.HasSubscribedTopics)
|
||||||
{
|
{
|
||||||
@@ -416,11 +441,16 @@ namespace MQTTnet.Server
|
|||||||
clientSession.AddSubscribedTopic(topic);
|
clientSession.AddSubscribedTopic(topic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sessionsManagementLock.ExitWriteLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnSubscriptionsRemoved(MqttSession clientSession, List<string> subscriptionTopics)
|
public void OnSubscriptionsRemoved(MqttSession clientSession, List<string> subscriptionTopics)
|
||||||
{
|
{
|
||||||
lock (_sessionsManagementLock)
|
_sessionsManagementLock.EnterWriteLock();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
foreach (var subscriptionTopic in subscriptionTopics)
|
foreach (var subscriptionTopic in subscriptionTopics)
|
||||||
{
|
{
|
||||||
@@ -433,13 +463,17 @@ namespace MQTTnet.Server
|
|||||||
_subscriberSessions.Remove(clientSession);
|
_subscriberSessions.Remove(clientSession);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sessionsManagementLock.ExitWriteLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
if (!_options.EnablePersistentSessions)
|
if (!_options.EnablePersistentSessions)
|
||||||
{
|
{
|
||||||
_sessions.Clear();
|
_sessionsStorage.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,50 +567,59 @@ namespace MQTTnet.Server
|
|||||||
MqttSession oldSession;
|
MqttSession oldSession;
|
||||||
MqttClient oldClient;
|
MqttClient oldClient;
|
||||||
|
|
||||||
lock (_sessionsManagementLock)
|
_sessionsManagementLock.EnterWriteLock();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
MqttSession session;
|
MqttSession session;
|
||||||
|
|
||||||
// Create a new session (if required).
|
// Create a new session (if required).
|
||||||
if (!_sessions.TryGetValue(connectPacket.ClientId, out oldSession))
|
if (!_sessionsStorage.TryGetSession(connectPacket.ClientId, out oldSession))
|
||||||
{
|
{
|
||||||
session = CreateSession(connectPacket.ClientId, validatingConnectionEventArgs.SessionItems, sessionShouldPersist);
|
session = CreateSession(connectPacket, validatingConnectionEventArgs, sessionShouldPersist);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (connectPacket.CleanSession)
|
if (connectPacket.CleanSession)
|
||||||
{
|
{
|
||||||
_logger.Verbose("Deleting existing session of client '{0}' due to clean start.", connectPacket.ClientId);
|
_logger.Verbose("Deleting existing session of client '{0}' due to clean start", connectPacket.ClientId);
|
||||||
_subscriberSessions.Remove(oldSession);
|
_subscriberSessions.Remove(oldSession);
|
||||||
session = CreateSession(connectPacket.ClientId, validatingConnectionEventArgs.SessionItems, sessionShouldPersist);
|
session = CreateSession(connectPacket, validatingConnectionEventArgs, sessionShouldPersist);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId);
|
_logger.Verbose("Reusing existing session of client '{0}'", connectPacket.ClientId);
|
||||||
session = oldSession;
|
session = oldSession;
|
||||||
oldSession = null;
|
oldSession = null;
|
||||||
|
|
||||||
// Session persistence could change for MQTT 5 clients that reconnect with different SessionExpiryInterval
|
// Session persistence could change for MQTT 5 clients that reconnect with different SessionExpiryInterval
|
||||||
session.IsPersistent = sessionShouldPersist;
|
session.IsPersistent = sessionShouldPersist;
|
||||||
connAckPacket.IsSessionPresent = true;
|
session.DisconnectedTimestamp = null;
|
||||||
session.Recover();
|
session.Recover();
|
||||||
|
|
||||||
|
connAckPacket.IsSessionPresent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_sessions[connectPacket.ClientId] = session;
|
_sessionsStorage.UpdateSession(connectPacket.ClientId, session);
|
||||||
|
|
||||||
// Create a new client (always required).
|
// Create a new client (always required).
|
||||||
_clients.TryGetValue(connectPacket.ClientId, out oldClient);
|
lock (_clients)
|
||||||
if (oldClient != null)
|
|
||||||
{
|
{
|
||||||
// This will stop the current client from sending and receiving but remains the connection active
|
_clients.TryGetValue(connectPacket.ClientId, out oldClient);
|
||||||
// for a later DISCONNECT packet.
|
if (oldClient != null)
|
||||||
oldClient.IsTakenOver = true;
|
{
|
||||||
|
// This will stop the current client from sending and receiving but remains the connection active
|
||||||
|
// for a later DISCONNECT packet.
|
||||||
|
oldClient.IsTakenOver = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
client = CreateClient(connectPacket, channelAdapter, session);
|
||||||
|
_clients[connectPacket.ClientId] = client;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
client = CreateClient(connectPacket, channelAdapter, session);
|
finally
|
||||||
|
{
|
||||||
_clients[connectPacket.ClientId] = client;
|
_sessionsManagementLock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!connAckPacket.IsSessionPresent)
|
if (!connAckPacket.IsSessionPresent)
|
||||||
@@ -592,12 +635,7 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
if (_eventContainer.ClientDisconnectedEvent.HasHandlers)
|
if (_eventContainer.ClientDisconnectedEvent.HasHandlers)
|
||||||
{
|
{
|
||||||
var eventArgs = new ClientDisconnectedEventArgs(
|
var eventArgs = new ClientDisconnectedEventArgs(oldClient.Id, null, MqttClientDisconnectType.Takeover, oldClient.Endpoint, oldClient.Session.Items);
|
||||||
oldClient.Id,
|
|
||||||
null,
|
|
||||||
MqttClientDisconnectType.Takeover,
|
|
||||||
oldClient.Endpoint,
|
|
||||||
oldClient.Session.Items);
|
|
||||||
|
|
||||||
await _eventContainer.ClientDisconnectedEvent.TryInvokeAsync(eventArgs, _logger).ConfigureAwait(false);
|
await _eventContainer.ClientDisconnectedEvent.TryInvokeAsync(eventArgs, _logger).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -609,11 +647,14 @@ namespace MQTTnet.Server
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttSession CreateSession(string clientId, IDictionary sessionItems, bool isPersistent)
|
MqttSession CreateSession(MqttConnectPacket connectPacket, ValidatingConnectionEventArgs validatingConnectionEventArgs, bool isPersistent)
|
||||||
{
|
{
|
||||||
_logger.Verbose("Created new session for client '{0}'.", clientId);
|
_logger.Verbose("Created new session for client '{0}'", connectPacket.ClientId);
|
||||||
|
|
||||||
return new MqttSession(clientId, isPersistent, sessionItems, _options, _eventContainer, _retainedMessagesManager, this);
|
return new MqttSession(connectPacket, validatingConnectionEventArgs.SessionItems, _options, _eventContainer, _retainedMessagesManager, this)
|
||||||
|
{
|
||||||
|
IsPersistent = isPersistent
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task FireApplicationMessageNotConsumedEvent(MqttApplicationMessage applicationMessage, string senderId)
|
async Task FireApplicationMessageNotConsumedEvent(MqttApplicationMessage applicationMessage, string senderId)
|
||||||
@@ -629,15 +670,20 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
MqttSession GetClientSession(string clientId)
|
MqttSession GetClientSession(string clientId)
|
||||||
{
|
{
|
||||||
lock (_sessionsManagementLock)
|
_sessionsManagementLock.EnterReadLock();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (!_sessions.TryGetValue(clientId, out var session))
|
if (!_sessionsStorage.TryGetSession(clientId, out var session))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Client session '{clientId}' is unknown.");
|
throw new InvalidOperationException($"Client session '{clientId}' is unknown.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sessionsManagementLock.ExitReadLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttConnectPacket> ReceiveConnectPacket(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
async Task<MqttConnectPacket> ReceiveConnectPacket(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace MQTTnet.Server
|
|||||||
readonly Dictionary<string, MqttSubscription> _subscriptions = new Dictionary<string, MqttSubscription>();
|
readonly Dictionary<string, MqttSubscription> _subscriptions = new Dictionary<string, MqttSubscription>();
|
||||||
|
|
||||||
// Use subscription lock to maintain consistency across subscriptions and topic hash dictionaries
|
// Use subscription lock to maintain consistency across subscriptions and topic hash dictionaries
|
||||||
readonly AsyncLock _subscriptionsLock = new AsyncLock();
|
readonly ReaderWriterLockSlim _subscriptionsLock = new ReaderWriterLockSlim();
|
||||||
readonly Dictionary<ulong, TopicHashMaskSubscriptions> _wildcardSubscriptionsByTopicHash = new Dictionary<ulong, TopicHashMaskSubscriptions>();
|
readonly Dictionary<ulong, TopicHashMaskSubscriptions> _wildcardSubscriptionsByTopicHash = new Dictionary<ulong, TopicHashMaskSubscriptions>();
|
||||||
|
|
||||||
public MqttClientSubscriptionsManager(
|
public MqttClientSubscriptionsManager(
|
||||||
@@ -51,7 +51,8 @@ namespace MQTTnet.Server
|
|||||||
var possibleSubscriptions = new List<MqttSubscription>();
|
var possibleSubscriptions = new List<MqttSubscription>();
|
||||||
|
|
||||||
// Check for possible subscriptions. They might have collisions but this is fine.
|
// Check for possible subscriptions. They might have collisions but this is fine.
|
||||||
using (_subscriptionsLock.EnterAsync(CancellationToken.None).GetAwaiter().GetResult())
|
_subscriptionsLock.EnterReadLock();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (_noWildcardSubscriptionsByTopicHash.TryGetValue(topicHash, out var noWildcardSubscriptions))
|
if (_noWildcardSubscriptionsByTopicHash.TryGetValue(topicHash, out var noWildcardSubscriptions))
|
||||||
{
|
{
|
||||||
@@ -73,6 +74,10 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_subscriptionsLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
// The pre check has evaluated that nothing is subscribed.
|
// The pre check has evaluated that nothing is subscribed.
|
||||||
// If there were some possible candidates they get checked below
|
// If there were some possible candidates they get checked below
|
||||||
@@ -157,7 +162,7 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_subscriptionsLock.Dispose();
|
_subscriptionsLock?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SubscribeResult> Subscribe(MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
|
public async Task<SubscribeResult> Subscribe(MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
|
||||||
@@ -177,15 +182,15 @@ namespace MQTTnet.Server
|
|||||||
// lower one.
|
// lower one.
|
||||||
foreach (var topicFilterItem in subscribePacket.TopicFilters.OrderByDescending(f => f.QualityOfServiceLevel))
|
foreach (var topicFilterItem in subscribePacket.TopicFilters.OrderByDescending(f => f.QualityOfServiceLevel))
|
||||||
{
|
{
|
||||||
var subscriptionEventArgs = await InterceptSubscribe(topicFilterItem, cancellationToken).ConfigureAwait(false);
|
var interceptorEventArgs = await InterceptSubscribe(topicFilterItem, subscribePacket.UserProperties, cancellationToken).ConfigureAwait(false);
|
||||||
var topicFilter = subscriptionEventArgs.TopicFilter;
|
var topicFilter = interceptorEventArgs.TopicFilter;
|
||||||
var processSubscription = subscriptionEventArgs.ProcessSubscription && subscriptionEventArgs.Response.ReasonCode <= MqttSubscribeReasonCode.GrantedQoS2;
|
var processSubscription = interceptorEventArgs.ProcessSubscription && interceptorEventArgs.Response.ReasonCode <= MqttSubscribeReasonCode.GrantedQoS2;
|
||||||
|
|
||||||
result.UserProperties = subscriptionEventArgs.UserProperties;
|
result.UserProperties = interceptorEventArgs.UserProperties;
|
||||||
result.ReasonString = subscriptionEventArgs.ReasonString;
|
result.ReasonString = interceptorEventArgs.ReasonString;
|
||||||
result.ReasonCodes.Add(subscriptionEventArgs.Response.ReasonCode);
|
result.ReasonCodes.Add(interceptorEventArgs.Response.ReasonCode);
|
||||||
|
|
||||||
if (subscriptionEventArgs.CloseConnection)
|
if (interceptorEventArgs.CloseConnection)
|
||||||
{
|
{
|
||||||
// When any of the interceptor calls leads to a connection close the connection
|
// When any of the interceptor calls leads to a connection close the connection
|
||||||
// must be closed. So do not revert to false!
|
// must be closed. So do not revert to false!
|
||||||
@@ -197,7 +202,7 @@ namespace MQTTnet.Server
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var createSubscriptionResult = CreateSubscription(topicFilter, subscribePacket.SubscriptionIdentifier, subscriptionEventArgs.Response.ReasonCode);
|
var createSubscriptionResult = CreateSubscription(topicFilter, subscribePacket.SubscriptionIdentifier, interceptorEventArgs.Response.ReasonCode);
|
||||||
|
|
||||||
addedSubscriptions.Add(topicFilter.Topic);
|
addedSubscriptions.Add(topicFilter.Topic);
|
||||||
finalTopicFilters.Add(topicFilter);
|
finalTopicFilters.Add(topicFilter);
|
||||||
@@ -232,18 +237,20 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
var removedSubscriptions = new List<string>();
|
var removedSubscriptions = new List<string>();
|
||||||
|
|
||||||
using (await _subscriptionsLock.EnterAsync(cancellationToken).ConfigureAwait(false))
|
_subscriptionsLock.EnterWriteLock();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
||||||
{
|
{
|
||||||
_subscriptions.TryGetValue(topicFilter, out var existingSubscription);
|
_subscriptions.TryGetValue(topicFilter, out var existingSubscription);
|
||||||
|
|
||||||
var interceptorContext = await InterceptUnsubscribe(topicFilter, existingSubscription, cancellationToken).ConfigureAwait(false);
|
var interceptorEventArgs = await InterceptUnsubscribe(topicFilter, existingSubscription, unsubscribePacket.UserProperties, cancellationToken).ConfigureAwait(false);
|
||||||
var acceptUnsubscription = interceptorContext.Response.ReasonCode == MqttUnsubscribeReasonCode.Success;
|
var acceptUnsubscription = interceptorEventArgs.Response.ReasonCode == MqttUnsubscribeReasonCode.Success;
|
||||||
|
|
||||||
result.ReasonCodes.Add(interceptorContext.Response.ReasonCode);
|
result.UserProperties = interceptorEventArgs.UserProperties;
|
||||||
|
result.ReasonCodes.Add(interceptorEventArgs.Response.ReasonCode);
|
||||||
|
|
||||||
if (interceptorContext.CloseConnection)
|
if (interceptorEventArgs.CloseConnection)
|
||||||
{
|
{
|
||||||
// When any of the interceptor calls leads to a connection close the connection
|
// When any of the interceptor calls leads to a connection close the connection
|
||||||
// must be closed. So do not revert to false!
|
// must be closed. So do not revert to false!
|
||||||
@@ -255,7 +262,7 @@ namespace MQTTnet.Server
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interceptorContext.ProcessUnsubscription)
|
if (interceptorEventArgs.ProcessUnsubscription)
|
||||||
{
|
{
|
||||||
_subscriptions.Remove(topicFilter);
|
_subscriptions.Remove(topicFilter);
|
||||||
|
|
||||||
@@ -292,8 +299,11 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
_subscriptionChangedNotification?.OnSubscriptionsRemoved(_session, removedSubscriptions);
|
{
|
||||||
|
_subscriptionsLock.ExitWriteLock();
|
||||||
|
_subscriptionChangedNotification?.OnSubscriptionsRemoved(_session, removedSubscriptions);
|
||||||
|
}
|
||||||
|
|
||||||
if (_eventContainer.ClientUnsubscribedTopicEvent.HasHandlers)
|
if (_eventContainer.ClientUnsubscribedTopicEvent.HasHandlers)
|
||||||
{
|
{
|
||||||
@@ -340,9 +350,10 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
// Add to subscriptions and maintain topic hash dictionaries
|
// Add to subscriptions and maintain topic hash dictionaries
|
||||||
|
|
||||||
using (_subscriptionsLock.EnterAsync(CancellationToken.None).GetAwaiter().GetResult())
|
_subscriptionsLock.EnterWriteLock();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
MqttSubscription.CalculateTopicHash(topicFilter.Topic, out var topicHash, out var topicHashMask, out var hasWildcard);
|
MqttTopicHash.Calculate(topicFilter.Topic, out var topicHash, out _, out var hasWildcard);
|
||||||
|
|
||||||
if (_subscriptions.TryGetValue(topicFilter.Topic, out var existingSubscription))
|
if (_subscriptions.TryGetValue(topicFilter.Topic, out var existingSubscription))
|
||||||
{
|
{
|
||||||
@@ -390,6 +401,10 @@ namespace MQTTnet.Server
|
|||||||
subscriptions.Add(subscription);
|
subscriptions.Add(subscription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_subscriptionsLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
return new CreateSubscriptionResult
|
return new CreateSubscriptionResult
|
||||||
{
|
{
|
||||||
@@ -452,9 +467,12 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<InterceptingSubscriptionEventArgs> InterceptSubscribe(MqttTopicFilter topicFilter, CancellationToken cancellationToken)
|
async Task<InterceptingSubscriptionEventArgs> InterceptSubscribe(
|
||||||
|
MqttTopicFilter topicFilter,
|
||||||
|
List<MqttUserProperty> userProperties,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var eventArgs = new InterceptingSubscriptionEventArgs(cancellationToken, _session.Id, new MqttSessionStatus(_session), topicFilter);
|
var eventArgs = new InterceptingSubscriptionEventArgs(cancellationToken, _session.Id, new MqttSessionStatus(_session), topicFilter, userProperties);
|
||||||
|
|
||||||
if (topicFilter.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
if (topicFilter.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||||
{
|
{
|
||||||
@@ -481,9 +499,13 @@ namespace MQTTnet.Server
|
|||||||
return eventArgs;
|
return eventArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<InterceptingUnsubscriptionEventArgs> InterceptUnsubscribe(string topicFilter, MqttSubscription mqttSubscription, CancellationToken cancellationToken)
|
async Task<InterceptingUnsubscriptionEventArgs> InterceptUnsubscribe(
|
||||||
|
string topicFilter,
|
||||||
|
MqttSubscription mqttSubscription,
|
||||||
|
List<MqttUserProperty> userProperties,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var clientUnsubscribingTopicEventArgs = new InterceptingUnsubscriptionEventArgs(cancellationToken, _session.Id, _session.Items, topicFilter)
|
var clientUnsubscribingTopicEventArgs = new InterceptingUnsubscriptionEventArgs(cancellationToken, _session.Id, _session.Items, topicFilter, userProperties)
|
||||||
{
|
{
|
||||||
Response =
|
Response =
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
_eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
|
_eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
|
||||||
|
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
if (logger == null)
|
||||||
|
throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
_logger = logger.WithSource(nameof(MqttRetainedMessagesManager));
|
_logger = logger.WithSource(nameof(MqttRetainedMessagesManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,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)
|
||||||
{
|
{
|
||||||
@@ -83,7 +86,7 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || !existingMessage.Payload.SequenceEqual(applicationMessage.Payload ?? EmptyBuffer.Array))
|
if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || !SequenceEqual(existingMessage.PayloadSegment, payloadSegment))
|
||||||
{
|
{
|
||||||
_messages[applicationMessage.Topic] = applicationMessage;
|
_messages[applicationMessage.Topic] = applicationMessage;
|
||||||
saveIsRequired = true;
|
saveIsRequired = true;
|
||||||
@@ -123,6 +126,19 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<MqttApplicationMessage> GetMessage(string topic)
|
||||||
|
{
|
||||||
|
lock (_messages)
|
||||||
|
{
|
||||||
|
if (_messages.TryGetValue(topic, out var message))
|
||||||
|
{
|
||||||
|
return Task.FromResult(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult<MqttApplicationMessage>(null);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ClearMessages()
|
public async Task ClearMessages()
|
||||||
{
|
{
|
||||||
lock (_messages)
|
lock (_messages)
|
||||||
@@ -135,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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,8 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public AsyncEvent<ClientUnsubscribedTopicEventArgs> ClientUnsubscribedTopicEvent { get; } = new AsyncEvent<ClientUnsubscribedTopicEventArgs>();
|
public AsyncEvent<ClientUnsubscribedTopicEventArgs> ClientUnsubscribedTopicEvent { get; } = new AsyncEvent<ClientUnsubscribedTopicEventArgs>();
|
||||||
|
|
||||||
|
public AsyncEvent<InterceptingClientApplicationMessageEnqueueEventArgs> InterceptingClientEnqueueEvent { get; } = new AsyncEvent<InterceptingClientApplicationMessageEnqueueEventArgs>();
|
||||||
|
|
||||||
public AsyncEvent<InterceptingPacketEventArgs> InterceptingInboundPacketEvent { get; } = new AsyncEvent<InterceptingPacketEventArgs>();
|
public AsyncEvent<InterceptingPacketEventArgs> InterceptingInboundPacketEvent { get; } = new AsyncEvent<InterceptingPacketEventArgs>();
|
||||||
|
|
||||||
public AsyncEvent<InterceptingPacketEventArgs> InterceptingOutboundPacketEvent { get; } = new AsyncEvent<InterceptingPacketEventArgs>();
|
public AsyncEvent<InterceptingPacketEventArgs> InterceptingOutboundPacketEvent { get; } = new AsyncEvent<InterceptingPacketEventArgs>();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace MQTTnet.Server
|
|||||||
readonly MqttPacketBus _packetBus = new MqttPacketBus();
|
readonly MqttPacketBus _packetBus = new MqttPacketBus();
|
||||||
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
|
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
|
||||||
|
|
||||||
|
readonly MqttConnectPacket _connectPacket;
|
||||||
readonly MqttServerOptions _serverOptions;
|
readonly MqttServerOptions _serverOptions;
|
||||||
readonly MqttClientSubscriptionsManager _subscriptionsManager;
|
readonly MqttClientSubscriptionsManager _subscriptionsManager;
|
||||||
|
|
||||||
@@ -31,18 +32,16 @@ namespace MQTTnet.Server
|
|||||||
HashSet<string> _subscribedTopics;
|
HashSet<string> _subscribedTopics;
|
||||||
|
|
||||||
public MqttSession(
|
public MqttSession(
|
||||||
string clientId,
|
MqttConnectPacket connectPacket,
|
||||||
bool isPersistent,
|
|
||||||
IDictionary items,
|
IDictionary items,
|
||||||
MqttServerOptions serverOptions,
|
MqttServerOptions serverOptions,
|
||||||
MqttServerEventContainer eventContainer,
|
MqttServerEventContainer eventContainer,
|
||||||
MqttRetainedMessagesManager retainedMessagesManager,
|
MqttRetainedMessagesManager retainedMessagesManager,
|
||||||
MqttClientSessionsManager clientSessionsManager)
|
MqttClientSessionsManager clientSessionsManager)
|
||||||
{
|
{
|
||||||
Id = clientId ?? throw new ArgumentNullException(nameof(clientId));
|
|
||||||
IsPersistent = isPersistent;
|
|
||||||
Items = items ?? throw new ArgumentNullException(nameof(items));
|
Items = items ?? throw new ArgumentNullException(nameof(items));
|
||||||
|
|
||||||
|
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
|
||||||
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
|
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
|
||||||
_clientSessionsManager = clientSessionsManager ?? throw new ArgumentNullException(nameof(clientSessionsManager));
|
_clientSessionsManager = clientSessionsManager ?? throw new ArgumentNullException(nameof(clientSessionsManager));
|
||||||
|
|
||||||
@@ -51,9 +50,13 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public DateTime CreatedTimestamp { get; } = DateTime.UtcNow;
|
public DateTime CreatedTimestamp { get; } = DateTime.UtcNow;
|
||||||
|
|
||||||
|
public DateTime? DisconnectedTimestamp { get; set; }
|
||||||
|
|
||||||
public bool HasSubscribedTopics => _subscribedTopics != null && _subscribedTopics.Count > 0;
|
public bool HasSubscribedTopics => _subscribedTopics != null && _subscribedTopics.Count > 0;
|
||||||
|
|
||||||
public string Id { get; }
|
public uint ExpiryInterval => _connectPacket.SessionExpiryInterval;
|
||||||
|
|
||||||
|
public string Id => _connectPacket.ClientId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Session should persist if CleanSession was set to false (Mqtt3) or if SessionExpiryInterval != 0 (Mqtt5)
|
/// Session should persist if CleanSession was set to false (Mqtt3) or if SessionExpiryInterval != 0 (Mqtt5)
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// 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.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace MQTTnet.Server
|
||||||
|
{
|
||||||
|
public sealed class MqttSessionsStorage
|
||||||
|
{
|
||||||
|
readonly Dictionary<string, MqttSession> _sessions = new Dictionary<string, MqttSession>(4096);
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
// Make sure that the sessions are also getting disposed!
|
||||||
|
// This object can be reused after disposing.
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var sessionItem in _sessions)
|
||||||
|
{
|
||||||
|
sessionItem.Value.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sessions.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyCollection<MqttSession> ReadAllSessions()
|
||||||
|
{
|
||||||
|
return new ReadOnlyCollection<MqttSession>(_sessions.Values.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetSession(string id, out MqttSession session)
|
||||||
|
{
|
||||||
|
if (id == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_sessions.TryGetValue(id, out session))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the Session Expiry Interval is 0xFFFFFFFF (UINT_MAX), the Session does not expire.
|
||||||
|
if (session.ExpiryInterval > 0 && session.ExpiryInterval != uint.MaxValue)
|
||||||
|
{
|
||||||
|
if (session.DisconnectedTimestamp.HasValue)
|
||||||
|
{
|
||||||
|
if (DateTime.UtcNow > session.DisconnectedTimestamp.Value.AddSeconds(session.ExpiryInterval))
|
||||||
|
{
|
||||||
|
TryRemoveSession(id, out var oldSession);
|
||||||
|
oldSession?.Dispose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryRemoveSession(string id, out MqttSession session)
|
||||||
|
{
|
||||||
|
if (id == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_sessions.TryGetValue(id, out session))
|
||||||
|
{
|
||||||
|
_sessions.Remove(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateSession(string id, MqttSession session)
|
||||||
|
{
|
||||||
|
if (id == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
_sessions[id] = session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user