mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-28 09:12:37 +08:00
v4.0.0 (#1357)
This commit is contained in:
@@ -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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Assigned_Client_ID_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public Task Connect_With_No_Client_Id()
|
||||
{
|
||||
return Connect_With_Client_Id("test_123", "test_123", null, "test_123");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Connect_With_Client_Id()
|
||||
{
|
||||
return Connect_With_Client_Id("Connect_With_Client_Id_test_456", null, "test_456", null);
|
||||
}
|
||||
|
||||
async Task Connect_With_Client_Id(string expectedClientId, string expectedReturnedClientId, string usedClientId, string assignedClientId)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
string serverConnectedClientId = null;
|
||||
string serverDisconnectedClientId = null;
|
||||
string clientAssignedClientId = null;
|
||||
|
||||
// Arrange server
|
||||
var disconnectedMre = new ManualResetEventSlim();
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(e.ClientId))
|
||||
{
|
||||
e.AssignedClientIdentifier = assignedClientId;
|
||||
e.ReasonCode = MqttConnectReasonCode.Success;
|
||||
}
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ClientConnectedAsync += args =>
|
||||
{
|
||||
serverConnectedClientId = args.ClientId;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ClientDisconnectedAsync += args =>
|
||||
{
|
||||
serverDisconnectedClientId = args.ClientId;
|
||||
disconnectedMre.Set();
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
// Arrange client
|
||||
var client = testEnvironment.CreateClient();
|
||||
client.ConnectedAsync += args =>
|
||||
{
|
||||
clientAssignedClientId = args.ConnectResult.AssignedClientIdentifier;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
// Act
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithClientId(usedClientId)
|
||||
.Build());
|
||||
|
||||
await client.DisconnectAsync();
|
||||
|
||||
// Wait for ClientDisconnectedHandler to trigger
|
||||
disconnectedMre.Wait(1000);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(expectedClientId, serverConnectedClientId);
|
||||
Assert.AreEqual(expectedClientId, serverDisconnectedClientId);
|
||||
Assert.AreEqual(expectedReturnedClientId, clientAssignedClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// 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.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Connection_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Close_Idle_Connection_On_Connect()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
|
||||
|
||||
var client = new CrossPlatformSocket(AddressFamily.InterNetwork);
|
||||
await client.ConnectAsync("localhost", testEnvironment.ServerPort, CancellationToken.None);
|
||||
|
||||
// Don't send anything. The server should close the connection.
|
||||
await Task.Delay(TimeSpan.FromSeconds(3));
|
||||
|
||||
try
|
||||
{
|
||||
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
|
||||
if (receivedBytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.Fail("Receive should throw an exception.");
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Send_Garbage()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
|
||||
|
||||
// Send an invalid packet and ensure that the server will close the connection and stay in a waiting state
|
||||
// forever. This is security related.
|
||||
var client = new CrossPlatformSocket(AddressFamily.InterNetwork);
|
||||
await client.ConnectAsync("localhost", testEnvironment.ServerPort, CancellationToken.None);
|
||||
|
||||
var buffer = Encoding.UTF8.GetBytes("Garbage");
|
||||
await client.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(3));
|
||||
|
||||
try
|
||||
{
|
||||
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
|
||||
if (receivedBytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.Fail("Receive should throw an exception.");
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
// 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.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Events_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Connected_Event()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientConnectedEventArgs eventArgs = null;
|
||||
server.ClientConnectedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Connected_Event)));
|
||||
Assert.IsTrue(eventArgs.Endpoint.Contains("127.0.0.1"));
|
||||
Assert.AreEqual(MqttProtocolVersion.V311, eventArgs.ProtocolVersion);
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Disconnected_Event()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientDisconnectedEventArgs eventArgs = null;
|
||||
server.ClientDisconnectedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||
await client.DisconnectAsync();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Disconnected_Event)));
|
||||
Assert.IsTrue(eventArgs.Endpoint.Contains("127.0.0.1"));
|
||||
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Subscribed_Event()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientSubscribedTopicEventArgs eventArgs = null;
|
||||
server.ClientSubscribedTopicAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.SubscribeAsync("The/Topic", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Subscribed_Event)));
|
||||
Assert.AreEqual("The/Topic", eventArgs.TopicFilter.Topic);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, eventArgs.TopicFilter.QualityOfServiceLevel);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Unsubscribed_Event()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientUnsubscribedTopicEventArgs eventArgs = null;
|
||||
server.ClientUnsubscribedTopicAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.UnsubscribeAsync("The/Topic");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Unsubscribed_Event)));
|
||||
Assert.AreEqual("The/Topic", eventArgs.TopicFilter);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Application_Message_Received_Event()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
InterceptingPublishEventArgs eventArgs = null;
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.PublishStringAsync("The_Topic", "The_Payload");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Application_Message_Received_Event)));
|
||||
Assert.AreEqual("The_Topic", eventArgs.ApplicationMessage.Topic);
|
||||
Assert.AreEqual("The_Payload", eventArgs.ApplicationMessage.ConvertPayloadToString());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Started_Event()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = testEnvironment.CreateServer(new MqttServerOptions());
|
||||
|
||||
EventArgs eventArgs = null;
|
||||
server.StartedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await server.StartAsync();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Stopped_Event()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
EventArgs eventArgs = null;
|
||||
server.StoppedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await server.StopAsync();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,64 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Injection_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Inject_Application_Message_At_Session_Level()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
var receiver1 = await testEnvironment.ConnectClient();
|
||||
var receiver2 = await testEnvironment.ConnectClient();
|
||||
var messageReceivedHandler1 = testEnvironment.CreateApplicationMessageHandler(receiver1);
|
||||
var messageReceivedHandler2 = testEnvironment.CreateApplicationMessageHandler(receiver2);
|
||||
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
|
||||
await receiver1.SubscribeAsync("#");
|
||||
await receiver2.SubscribeAsync("#");
|
||||
|
||||
await clientStatus.EnqueueApplicationMessageAsync(new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build());
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, messageReceivedHandler1.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual("InjectedOne", messageReceivedHandler1.ReceivedEventArgs[0].ApplicationMessage.Topic);
|
||||
|
||||
// The second receiver should NOT receive the message.
|
||||
Assert.AreEqual(0, messageReceivedHandler2.ReceivedEventArgs.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Inject_ApplicationMessage_At_Server_Level()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var messageReceivedHandler = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
var injectedApplicationMessage = new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build();
|
||||
|
||||
await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(injectedApplicationMessage));
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, messageReceivedHandler.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual("InjectedOne", messageReceivedHandler.ReceivedEventArgs[0].ApplicationMessage.Topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class KeepAlive_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Client_DueTo_KeepAlive()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectLowLevelClient(o => o
|
||||
.WithTimeout(TimeSpan.FromSeconds(1))
|
||||
.WithTimeout(TimeSpan.Zero)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)).ConfigureAwait(false);
|
||||
|
||||
await client.SendAsync(new MqttConnectPacket
|
||||
{
|
||||
CleanSession = true,
|
||||
ClientId = "Disconnect_Client_DueTo_KeepAlive",
|
||||
KeepAlivePeriod = 1
|
||||
}, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
var responsePacket = await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
Assert.IsTrue(responsePacket is MqttConnAckPacket);
|
||||
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
|
||||
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||
responsePacket = await client.ReceiveAsync(CancellationToken.None);
|
||||
Assert.IsTrue(responsePacket is MqttPingRespPacket);
|
||||
}
|
||||
|
||||
// If we reach this point everything works as expected (server did not close the connection
|
||||
// due to proper ping messages.
|
||||
// Now we will wait 1.1 seconds because the server MUST wait 1.5 seconds in total (See spec).
|
||||
|
||||
await Task.Delay(1100);
|
||||
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||
responsePacket = await client.ReceiveAsync(CancellationToken.None);
|
||||
Assert.IsTrue(responsePacket is MqttPingRespPacket);
|
||||
|
||||
// Now we will wait longer than 1.5 so that the server will close the connection.
|
||||
responsePacket = await client.ReceiveAsync(CancellationToken.None);
|
||||
|
||||
var disconnectPacket = responsePacket as MqttDisconnectPacket;
|
||||
|
||||
Assert.IsTrue(disconnectPacket != null);
|
||||
Assert.AreEqual(disconnectPacket.ReasonCode, MqttDisconnectReasonCode.KeepAliveTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Load_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Handle_100_000_Messages_In_Receiving_Client()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receivedMessages = 0;
|
||||
|
||||
using (var receiverClient = await testEnvironment.ConnectClient())
|
||||
{
|
||||
receiverClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await receiverClient.SubscribeAsync("t/+");
|
||||
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (var client = await testEnvironment.ConnectClient())
|
||||
{
|
||||
var applicationMessageBuilder = new MqttApplicationMessageBuilder();
|
||||
|
||||
for (var j = 0; j < 1000; j++)
|
||||
{
|
||||
var message = applicationMessageBuilder.WithTopic("t/" + j)
|
||||
.Build();
|
||||
|
||||
await client.PublishAsync(message)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await client.DisconnectAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(60));
|
||||
|
||||
Assert.AreEqual(100000, receivedMessages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Handle_100_000_Messages_In_Low_Level_Client()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var receivedMessages = 0;
|
||||
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var client = await testEnvironment.ConnectLowLevelClient())
|
||||
{
|
||||
await client.SendAsync(
|
||||
new MqttConnectPacket
|
||||
{
|
||||
ClientId = "Handle_100_000_Messages_In_Low_Level_Client_" + Guid.NewGuid()
|
||||
}, CancellationToken.None);
|
||||
|
||||
var packet = await client.ReceiveAsync(CancellationToken.None);
|
||||
|
||||
var connAckPacket = packet as MqttConnAckPacket;
|
||||
|
||||
Assert.IsTrue(connAckPacket != null);
|
||||
Assert.AreEqual(MqttConnectReasonCode.Success, connAckPacket.ReasonCode);
|
||||
|
||||
var publishPacket = new MqttPublishPacket();
|
||||
|
||||
for (var j = 0; j < 1000; j++)
|
||||
{
|
||||
publishPacket.Topic = j.ToString();
|
||||
|
||||
await client.SendAsync(publishPacket, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await client.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(10));
|
||||
|
||||
Assert.AreEqual(100000, receivedMessages);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Handle_100_000_Messages_In_Server()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var receivedMessages = 0;
|
||||
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (var client = await testEnvironment.ConnectClient())
|
||||
{
|
||||
var applicationMessageBuilder = new MqttApplicationMessageBuilder();
|
||||
|
||||
for (var j = 0; j < 1000; j++)
|
||||
{
|
||||
var message = applicationMessageBuilder.WithTopic(j.ToString())
|
||||
.Build();
|
||||
|
||||
await client.PublishAsync(message)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await client.DisconnectAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(10));
|
||||
|
||||
Assert.AreEqual(100000, receivedMessages);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// 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.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttSubscriptionsManager_Tests : BaseTestClass
|
||||
{
|
||||
MqttClientSubscriptionsManager _subscriptionsManager;
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeAndUnsubscribeSingle()
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()
|
||||
}
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
|
||||
Assert.IsTrue(CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
|
||||
|
||||
var up = new MqttUnsubscribePacket();
|
||||
up.TopicFilters.Add("A/B/C");
|
||||
await _subscriptionsManager.Unsubscribe(up, CancellationToken.None);
|
||||
|
||||
Assert.IsFalse(CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeDifferentQoSSuccess()
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }
|
||||
}
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
|
||||
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeSingleNoSuccess()
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()
|
||||
}
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
|
||||
Assert.IsFalse(CheckSubscriptions("A/B/X", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeSingleSuccess()
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()
|
||||
}
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
|
||||
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "");
|
||||
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeTwoTimesSuccess()
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilter { Topic = "#", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce },
|
||||
new MqttTopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }
|
||||
}
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
|
||||
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
|
||||
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
var logger = new TestLogger();
|
||||
var options = new MqttServerOptions();
|
||||
var retainedMessagesManager = new MqttRetainedMessagesManager(new MqttServerEventContainer(), logger);
|
||||
var eventContainer = new MqttServerEventContainer();
|
||||
var clientSessionManager = new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger);
|
||||
|
||||
var session = new MqttSession("", false, new ConcurrentDictionary<object, object>(), options, eventContainer, retainedMessagesManager, clientSessionManager);
|
||||
|
||||
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerEventContainer(), retainedMessagesManager, clientSessionManager);
|
||||
}
|
||||
|
||||
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
|
||||
{
|
||||
ulong topicHashMask; // not needed
|
||||
bool hasWildcard; // not needed
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out topicHashMask, out hasWildcard);
|
||||
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class No_Local_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public Task Subscribe_With_No_Local()
|
||||
{
|
||||
return ExecuteTest(true, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Subscribe_Without_No_Local()
|
||||
{
|
||||
return ExecuteTest(false, 1);
|
||||
}
|
||||
|
||||
async Task ExecuteTest(
|
||||
bool noLocal,
|
||||
int expectedCountAfterPublish)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
var topicFilter = testEnvironment.Factory.CreateTopicFilterBuilder().WithTopic("Topic").WithNoLocal(noLocal).Build();
|
||||
await client1.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(0);
|
||||
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterPublish);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.Formatter;
|
||||
using MQTTnet.Client;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Retain_As_Published_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public Task Subscribe_With_Retain_As_Published()
|
||||
{
|
||||
return ExecuteTest(true);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Subscribe_Without_Retain_As_Published()
|
||||
{
|
||||
return ExecuteTest(false);
|
||||
}
|
||||
|
||||
async Task ExecuteTest(bool retainAsPublished)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
var topicFilter = testEnvironment.Factory.CreateTopicFilterBuilder().WithTopic("Topic").WithRetainAsPublished(retainAsPublished).Build();
|
||||
await client1.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(1);
|
||||
Assert.AreEqual(retainAsPublished, applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.Retain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// 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 Retain_Handling_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public Task Send_At_Subscribe()
|
||||
{
|
||||
return ExecuteTest(MqttRetainHandling.SendAtSubscribe, 1, 2, 3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Do_Not_Send_On_Subscribe()
|
||||
{
|
||||
return ExecuteTest(MqttRetainHandling.DoNotSendOnSubscribe, 0, 1, 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Send_At_Subscribe_If_New_Subscription_Only()
|
||||
{
|
||||
return ExecuteTest(MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly, 1, 2, 2);
|
||||
}
|
||||
|
||||
async Task ExecuteTest(
|
||||
MqttRetainHandling retainHandling,
|
||||
int expectedCountAfterSubscribe,
|
||||
int expectedCountAfterSecondPublish,
|
||||
int expectedCountAfterSecondSubscribe)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client2);
|
||||
|
||||
var topicFilter = testEnvironment.Factory.CreateTopicFilterBuilder().WithTopic("Topic").WithRetainHandling(retainHandling).Build();
|
||||
await client2.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSubscribe);
|
||||
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSecondPublish);
|
||||
|
||||
await client2.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSecondSubscribe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// 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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Retained_Messages_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Retained_Messages_Supported_V3()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.IsTrue(connectResult.RetainAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Retained_Messages_Supported_V5()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.IsTrue(connectResult.RetainAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Retained_Messages_Flow()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var retainedMessage = new MqttApplicationMessageBuilder().WithTopic("r").WithPayload("r").WithRetainFlag().Build();
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await c1.PublishAsync(retainedMessage);
|
||||
await c1.DisconnectAsync();
|
||||
await LongTestDelay();
|
||||
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
await c2.UnsubscribeAsync("r");
|
||||
await Task.Delay(100);
|
||||
messageHandler.AssertReceivedCountEquals(i);
|
||||
|
||||
await c2.SubscribeAsync("r");
|
||||
await Task.Delay(100);
|
||||
messageHandler.AssertReceivedCountEquals(i + 1);
|
||||
}
|
||||
|
||||
await c2.DisconnectAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_No_Retained_Message_After_Subscribe()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
await c2.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("retained_other").Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(0);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_Retained_Message_After_Subscribe()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await c2.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("retained").Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
Assert.IsTrue(messageHandler.ReceivedEventArgs.First().ApplicationMessage.Retain);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Clear_Retained_Message_With_Empty_Payload()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[0]).WithRetainFlag().Build());
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await Task.Delay(200);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(0);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Clear_Retained_Message_With_Null_Payload()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload((byte[])null).WithRetainFlag().Build());
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await Task.Delay(200);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// 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.Adapter;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Security_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Use_Username_Null_Password_Empty()
|
||||
{
|
||||
string username = null;
|
||||
var password = string.Empty;
|
||||
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
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));
|
||||
Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException));
|
||||
Assert.AreEqual("Error while authenticating. If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].", ex.Message, false);
|
||||
Assert.AreEqual(MqttClientConnectResultCode.UnspecifiedError, ex.ResultCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_UserName()
|
||||
{
|
||||
return TestCredentials("x", "Password1");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_Password()
|
||||
{
|
||||
return TestCredentials("UserName", "x");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_UserName_And_Password()
|
||||
{
|
||||
return TestCredentials("x", "x");
|
||||
}
|
||||
|
||||
private async Task TestCredentials(string userName, string password)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
if (e.Username != "UserName1")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
if (e.Password != "Password1")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
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));
|
||||
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, ex.Result.ResultCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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.Adapter;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Server_Reference_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_With_Reference_Server()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.ServerMoved;
|
||||
e.ServerReference = "new_server";
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.Build());
|
||||
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (MqttConnectingFailedException e)
|
||||
{
|
||||
Assert.AreEqual(MqttClientConnectResultCode.ServerMoved, e.ResultCode);
|
||||
Assert.AreEqual("new_server", e.Result.ServerReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
// 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.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
using MqttClient = MQTTnet.Client.MqttClient;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Session_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Clean_Session_Persistence()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
// Create server with persistent sessions enabled
|
||||
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
const string ClientId = "Client1";
|
||||
|
||||
// Create client with clean session and long session expiry interval
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient(
|
||||
o => o.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||
.WithCleanSession() // start and end with clean session
|
||||
.WithClientId(ClientId)
|
||||
.Build());
|
||||
|
||||
// Disconnect; empty session should be removed from server
|
||||
|
||||
await client1.DisconnectAsync();
|
||||
|
||||
// Simulate some time delay between connections
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Reconnect the same client ID without clean session
|
||||
|
||||
var client2 = testEnvironment.CreateClient();
|
||||
var options = testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||
.WithCleanSession(false) // see if there is a session
|
||||
.WithClientId(ClientId)
|
||||
.Build();
|
||||
|
||||
|
||||
var result = await client2.ConnectAsync(options).ConfigureAwait(false);
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
|
||||
// Session should NOT be present for MQTT v311 and initial CleanSession == true
|
||||
|
||||
Assert.IsTrue(!result.IsSessionPresent, "Session present");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Deleted_Event()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
// Arrange client and server.
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions(false));
|
||||
|
||||
var deletedEventFired = false;
|
||||
server.SessionDeletedAsync += e =>
|
||||
{
|
||||
deletedEventFired = true;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
// Act: Disconnect the client -> Event must be fired.
|
||||
await client.DisconnectAsync();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
// Assert that the event was fired properly.
|
||||
Assert.IsTrue(deletedEventFired);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Get_Session_Items_In_Status()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
// Don't validate anything. Just set some session items.
|
||||
e.SessionItems["can_subscribe_x"] = true;
|
||||
e.SessionItems["default_payload"] = "Hello World";
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await testEnvironment.ConnectClient();
|
||||
|
||||
var sessionStatus = await testEnvironment.Server.GetSessionsAsync();
|
||||
var session = sessionStatus.First();
|
||||
|
||||
Assert.AreEqual(true, session.Items["can_subscribe_x"]);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Handle_Parallel_Connection_Attempts()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var options = new MqttClientOptionsBuilder().WithClientId("1").WithKeepAlivePeriod(TimeSpan.FromSeconds(5));
|
||||
|
||||
var hasReceive = false;
|
||||
|
||||
void OnReceive()
|
||||
{
|
||||
if (!hasReceive)
|
||||
{
|
||||
hasReceive = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to connect 50 clients at the same time.
|
||||
var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(i => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
|
||||
var connectedClients = clients.Where(c => c?.IsConnected ?? false).ToList();
|
||||
|
||||
Assert.AreEqual(1, connectedClients.Count);
|
||||
|
||||
await Task.Delay(5000);
|
||||
|
||||
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
|
||||
var sendClient = await testEnvironment.ConnectClient(option2);
|
||||
await sendClient.PublishStringAsync("aaa", "1");
|
||||
|
||||
await Task.Delay(3000);
|
||||
|
||||
Assert.AreEqual(true, hasReceive);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Manage_Session_MaxParallel()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
var serverOptions = new MqttServerOptionsBuilder();
|
||||
await testEnvironment.StartServer(serverOptions);
|
||||
|
||||
var options = new MqttClientOptionsBuilder().WithClientId("1");
|
||||
|
||||
var clients = await Task.WhenAll(Enumerable.Range(0, 10).Select(i => TryConnect(testEnvironment, options)));
|
||||
|
||||
var connectedClients = clients.Where(c => c?.IsConnected ?? false).ToList();
|
||||
|
||||
Assert.AreEqual(1, connectedClients.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
|
||||
public async Task Retry_If_Not_PubAck(MqttQualityOfServiceLevel qos)
|
||||
{
|
||||
long count = 0;
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
var publisher = await testEnvironment.ConnectClient();
|
||||
|
||||
var subscriber = await testEnvironment.ConnectClient(o => o.WithClientId(qos.ToString()).WithCleanSession(false));
|
||||
|
||||
subscriber.ApplicationMessageReceivedAsync += c =>
|
||||
{
|
||||
c.AutoAcknowledge = false;
|
||||
++count;
|
||||
Console.WriteLine("process");
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await subscriber.SubscribeAsync("#", qos);
|
||||
|
||||
var pub = publisher.PublishStringAsync("a", null, qos);
|
||||
|
||||
await Task.Delay(100);
|
||||
await subscriber.DisconnectAsync();
|
||||
await subscriber.ConnectAsync(subscriber.Options);
|
||||
await Task.Delay(100);
|
||||
|
||||
var res = await pub;
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, res.ReasonCode);
|
||||
Assert.AreEqual(2, count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Set_Session_Item()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
// Don't validate anything. Just set some session items.
|
||||
e.SessionItems["can_subscribe_x"] = true;
|
||||
e.SessionItems["default_payload"] = "Hello World";
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
if (e.TopicFilter.Topic == "x")
|
||||
{
|
||||
if (e.SessionItems["can_subscribe_x"] as bool? == false)
|
||||
{
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
|
||||
}
|
||||
}
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
e.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(e.SessionItems["default_payload"] as string ?? string.Empty);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
string receivedPayload = null;
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var subscribeResult = await client.SubscribeAsync("x");
|
||||
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS0, subscribeResult.Items.First().ResultCode);
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
await client2.PublishStringAsync("x");
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.AreEqual("Hello World", receivedPayload);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<MqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sendClient = await testEnvironment.ConnectClient(options);
|
||||
|
||||
sendClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
onReceive();
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
using (var subscribeTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||
{
|
||||
await sendClient.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, subscribeTimeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return sendClient;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async Task<MqttClient> TryConnect(TestEnvironment testEnvironment, MqttClientOptionsBuilder options)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await testEnvironment.ConnectClient(options);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Shared_Subscriptions_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Shared_Subscriptions_Not_Supported()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.IsFalse(connectResult.SharedSubscriptionAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscription_Of_Shared_Subscription_Is_Denied()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
await client.ConnectAsync(testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
var subscribeResult = await client.SubscribeAsync("$share/A");
|
||||
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.SharedSubscriptionsNotSupported, subscribeResult.Items.First().ResultCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// 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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Status_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Show_Client_And_Session_Statistics()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1"));
|
||||
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client2"));
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
var sessionStatus = await server.GetSessionsAsync();
|
||||
|
||||
Assert.AreEqual(2, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c2.Options.ClientId));
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
await c2.DisconnectAsync();
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
sessionStatus = await server.GetSessionsAsync();
|
||||
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
Assert.AreEqual(0, sessionStatus.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Client()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1"));
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
|
||||
|
||||
await clientStatus.First().DisconnectAsync();
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsFalse(c1.IsConnected);
|
||||
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Keep_Persistent_Session()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1").WithCleanSession(false));
|
||||
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client2").WithCleanSession(false));
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
var sessionStatus = await server.GetSessionsAsync();
|
||||
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
|
||||
await c2.DisconnectAsync();
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
sessionStatus = await server.GetSessionsAsync();
|
||||
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Track_Sent_Application_Messages()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
for (var i = 1; i < 25; i++)
|
||||
{
|
||||
await c1.PublishStringAsync("a");
|
||||
await Task.Delay(50);
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount);
|
||||
Assert.AreEqual(0, clientStatus.First().ReceivedApplicationMessagesCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Track_Sent_Packets()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithNoKeepAlive());
|
||||
|
||||
for (var i = 1; i < 25; i++)
|
||||
{
|
||||
// At most once will send one packet to the client and the server will reply
|
||||
// with an additional ACK packet.
|
||||
await c1.PublishStringAsync("a", string.Empty, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
|
||||
Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount, "SAMC invalid!");
|
||||
|
||||
// + 1 because CONNECT is also counted.
|
||||
Assert.AreEqual(i + 1, clientStatus.First().SentPacketsCount, "SPC invalid!");
|
||||
|
||||
// +2 because ConnACK + PubAck package is already counted.
|
||||
Assert.AreEqual(i + 2, clientStatus.First().ReceivedPacketsCount, "RPC invalid!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
// 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.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Subscribe_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Intercept_Subscription()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
// Set the topic to "a" regards what the client wants to subscribe.
|
||||
e.TopicFilter.Topic = "a";
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var topicAReceived = false;
|
||||
var topicBReceived = false;
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
if (e.ApplicationMessage.Topic == "a")
|
||||
{
|
||||
topicAReceived = true;
|
||||
}
|
||||
else if (e.ApplicationMessage.Topic == "b")
|
||||
{
|
||||
topicBReceived = true;
|
||||
}
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await client.SubscribeAsync("b");
|
||||
|
||||
await client.PublishStringAsync("a");
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsTrue(topicAReceived);
|
||||
Assert.IsFalse(topicBReceived);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Unsubscribe()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c1"));
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c2"));
|
||||
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce).Build();
|
||||
await c2.PublishAsync(message);
|
||||
|
||||
await Task.Delay(500);
|
||||
Assert.AreEqual(0, receivedMessagesCount);
|
||||
|
||||
var subscribeEventCalled = false;
|
||||
server.ClientSubscribedTopicAsync += e =>
|
||||
{
|
||||
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == c1.Options.ClientId;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(subscribeEventCalled, "Subscribe event not called.");
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
await Task.Delay(250);
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
|
||||
var unsubscribeEventCalled = false;
|
||||
server.ClientUnsubscribedTopicAsync += e =>
|
||||
{
|
||||
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == c1.Options.ClientId;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.UnsubscribeAsync("a");
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(unsubscribeEventCalled, "Unsubscribe event not called.");
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
await Task.Delay(500);
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Multiple_In_Single_Request()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder()
|
||||
.WithTopicFilter("a")
|
||||
.WithTopicFilter("b")
|
||||
.WithTopicFilter("c")
|
||||
.Build());
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c2.PublishStringAsync("a");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 1);
|
||||
|
||||
await c2.PublishStringAsync("b");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 2);
|
||||
|
||||
await c2.PublishStringAsync("c");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 3);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Lots_In_Single_Request()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
optionsBuilder.WithTopicFilter(i.ToString());
|
||||
}
|
||||
|
||||
await c1.SubscribeAsync(optionsBuilder.Build()).ConfigureAwait(false);
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
var messageBuilder = new MqttApplicationMessageBuilder();
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
messageBuilder.WithTopic(i.ToString());
|
||||
|
||||
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessagesCount == 500, TimeSpan.FromSeconds(20));
|
||||
|
||||
Assert.AreEqual(500, receivedMessagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Lots_In_Multiple_Requests()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
var so = new MqttClientSubscribeOptionsBuilder()
|
||||
.WithTopicFilter(i.ToString()).Build();
|
||||
|
||||
await c1.SubscribeAsync(so).ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
var messageBuilder = new MqttApplicationMessageBuilder();
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
messageBuilder.WithTopic(i.ToString());
|
||||
|
||||
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessagesCount == 500, 5000);
|
||||
|
||||
Assert.AreEqual(500, receivedMessagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Multiple_In_Multiple_Request()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder()
|
||||
.WithTopicFilter("a")
|
||||
.Build());
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder()
|
||||
.WithTopicFilter("b")
|
||||
.Build());
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder()
|
||||
.WithTopicFilter("c")
|
||||
.Build());
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c2.PublishStringAsync("a");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 1);
|
||||
|
||||
await c2.PublishStringAsync("b");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 2);
|
||||
|
||||
await c2.PublishStringAsync("c");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 3);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Deny_Invalid_Topic()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
if (e.TopicFilter.Topic == "not_allowed_topic")
|
||||
{
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.TopicFilterInvalid;
|
||||
}
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var subscribeResult =await client.SubscribeAsync("allowed_topic");
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS0, subscribeResult.Items.First().ResultCode);
|
||||
|
||||
subscribeResult =await client.SubscribeAsync("not_allowed_topic");
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.TopicFilterInvalid, subscribeResult.Items.First().ResultCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Subscription_Identifier_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Subscription_Identifiers_Supported()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.IsTrue(connectResult.SubscriptionIdentifiersAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_With_Subscription_Identifier()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
var topicFilter = testEnvironment.Factory.CreateTopicFilterBuilder().WithTopic("Topic").Build();
|
||||
var subscribeOptions = testEnvironment.Factory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(456).WithTopicFilter(topicFilter).Build();
|
||||
|
||||
await client1.SubscribeAsync(subscribeOptions);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(0);
|
||||
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(1);
|
||||
|
||||
applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(456);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_With_Multiple_Subscription_Identifiers()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
|
||||
var topicFilter = testEnvironment.Factory.CreateTopicFilterBuilder().WithTopic("Topic/A").Build();
|
||||
var subscribeOptions = testEnvironment.Factory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(456).WithTopicFilter(topicFilter).Build();
|
||||
await client1.SubscribeAsync(subscribeOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
topicFilter = testEnvironment.Factory.CreateTopicFilterBuilder().WithTopic("Topic/+").Build();
|
||||
subscribeOptions = testEnvironment.Factory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(789).WithTopicFilter(topicFilter).Build();
|
||||
await client1.SubscribeAsync(subscribeOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(0);
|
||||
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic/A", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(1);
|
||||
|
||||
applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(456);
|
||||
applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(789);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,612 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public class Subscription_TopicHash_Tests
|
||||
{
|
||||
enum TopicHashSelector { NoWildcard, SingleWildcard, MultiWildcard };
|
||||
|
||||
MQTTnet.Server.MqttSession _clientSession;
|
||||
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_SingleWildCard()
|
||||
{
|
||||
var l0 = "pub0";
|
||||
var l1 = "topic1";
|
||||
var l2 = "+";
|
||||
var l3 = "prop1";
|
||||
var topic = string.Format("{0}/{1}/{2}/{3}", l0, l1, l2, l3);
|
||||
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
Assert.AreNotEqual(hashBytes[0], 0, "checksum 0 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[1], 0, "checksum 1 mismatch");
|
||||
Assert.AreEqual(hashBytes[2], 0, "checksum 2 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[3], 0, "checksum 3 mismatch");
|
||||
Assert.AreEqual(hashBytes[4], 0, "checksum 4 mismatch");
|
||||
Assert.AreEqual(hashBytes[5], 0, "checksum 5 mismatch");
|
||||
Assert.AreEqual(hashBytes[6], 0, "checksum 6 mismatch");
|
||||
Assert.AreEqual(hashBytes[7], 0, "checksum 7 mismatch");
|
||||
|
||||
// The mask should have zeroes where the wildcard and ff at the end
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
Assert.AreEqual(hashMaskBytes[0], 0xff, "mask 0 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[1], 0xff, "mask 1 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[2], 0, "mask 2 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[3], 0xff, "mask 3 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[4], 0xff, "mask 4 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[5], 0xff, "mask 5 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[6], 0xff, "mask 6 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[7], 0xff, "mask 7 mismatch");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_MultiWildCard()
|
||||
{
|
||||
var l0 = "pub0";
|
||||
var l1 = "topic1";
|
||||
var l2 = "sub1";
|
||||
var l3 = "#";
|
||||
var topic = string.Format("{0}/{1}/{2}/{3}", l0, l1, l2, l3);
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
Assert.AreNotEqual(hashBytes[0], 0, "checksum 0 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[1], 0, "checksum 1 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[2], 0, "checksum 2 mismatch");
|
||||
Assert.AreEqual(hashBytes[3], 0, "checksum 3 mismatch");
|
||||
Assert.AreEqual(hashBytes[4], 0, "checksum 4 mismatch");
|
||||
Assert.AreEqual(hashBytes[5], 0, "checksum 5 mismatch");
|
||||
Assert.AreEqual(hashBytes[6], 0, "checksum 6 mismatch");
|
||||
Assert.AreEqual(hashBytes[7], 0, "checksum 7 mismatch");
|
||||
|
||||
// The mask should have zeroes where the wildcard is and zero onward
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
Assert.AreEqual(hashMaskBytes[0], 0xff, "mask 0 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[1], 0xff, "mask 1 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[2], 0xff, "mask 2 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[3], 0, "mask 3 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[4], 0, "mask 4 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[5], 0, "mask 5 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[6], 0, "mask 6 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[7], 0, "mask 7 mismatch");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_NoWildCard()
|
||||
{
|
||||
var l0 = "pub0";
|
||||
var l1 = "topic1";
|
||||
var l2 = "sub1";
|
||||
var l3 = "prop1";
|
||||
var topic = string.Format("{0}/{1}/{2}/{3}", l0, l1, l2, l3);
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not wildcard present");
|
||||
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
Assert.AreNotEqual(hashBytes[0], 0, "checksum 0 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[1], 0, "checksum 1 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[2], 0, "checksum 2 mismatch");
|
||||
Assert.AreNotEqual(hashBytes[3], 0, "checksum 3 mismatch");
|
||||
Assert.AreEqual(hashBytes[4], 0, "checksum 4 mismatch");
|
||||
Assert.AreEqual(hashBytes[5], 0, "checksum 5 mismatch");
|
||||
Assert.AreEqual(hashBytes[6], 0, "checksum 6 mismatch");
|
||||
Assert.AreEqual(hashBytes[7], 0, "checksum 7 mismatch");
|
||||
|
||||
// The mask should have ff
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
Assert.AreEqual(hashMaskBytes[0], 0xff, "mask 0 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[1], 0xff, "mask 1 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[2], 0xff, "mask 2 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[3], 0xff, "mask 3 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[4], 0xff, "mask 4 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[5], 0xff, "mask 5 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[6], 0xff, "mask 6 mismatch");
|
||||
Assert.AreEqual(hashMaskBytes[7], 0xff, "mask 7 mismatch");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being #
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_MultiWildcard()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
const int NumLevels = 8;
|
||||
var levelNames = new string[NumLevels];
|
||||
for (var i = 0; i < NumLevels; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
sb.Append("/");
|
||||
string levelName;
|
||||
if (i == NumLevels - 1)
|
||||
{
|
||||
// last one is #
|
||||
levelName = "#";
|
||||
}
|
||||
else
|
||||
{
|
||||
levelName = "level" + i;
|
||||
}
|
||||
levelNames[i] = levelName;
|
||||
sb.Append(levelName);
|
||||
}
|
||||
var topic = sb.ToString();
|
||||
|
||||
// UInt64 is limited to 8 levels
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
// all bytes should contain checksum
|
||||
int count = 0;
|
||||
foreach (var h in hashBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreNotEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
// The mask should have ff except for last level
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
count = 0;
|
||||
foreach (var h in hashMaskBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreEqual(h, 0xff, "mask mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(h, 0, "last mask mismatch");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being +
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_SingleWildCard()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
const int NumLevels = 8;
|
||||
var levelNames = new string[NumLevels];
|
||||
for (var i = 0; i < NumLevels; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
sb.Append("/");
|
||||
string levelName;
|
||||
if (i == NumLevels - 1)
|
||||
{
|
||||
// last one is +
|
||||
levelName = "+";
|
||||
}
|
||||
else
|
||||
{
|
||||
levelName = "level" + i;
|
||||
}
|
||||
levelNames[i] = levelName;
|
||||
sb.Append(levelName);
|
||||
}
|
||||
var topic = sb.ToString();
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
// all bytes should contain checksum
|
||||
int count = 0;
|
||||
foreach (var h in hashBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreNotEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
// wildcard position
|
||||
Assert.AreEqual(h, 0, "checksum mismatch");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
// The mask should have ff
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
count = 0;
|
||||
foreach (var h in hashMaskBytes)
|
||||
{
|
||||
if (count < 7)
|
||||
{
|
||||
Assert.AreEqual(h, 0xff, "mask mismatch");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(h, 0, "last mask mismatch");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name exceeding 8 levels
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_NoWildCard()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
const int NumLevels = 9;
|
||||
var levelNames = new string[NumLevels];
|
||||
for(var i=0; i < NumLevels; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
sb.Append("/");
|
||||
var levelName = "level" + i;
|
||||
levelNames[i] = levelName;
|
||||
sb.Append(levelName);
|
||||
}
|
||||
var topic = sb.ToString();
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not present");
|
||||
|
||||
|
||||
var hashBytes = GetBytes(topicHash);
|
||||
// all bytes should contain checksum
|
||||
int count = 0;
|
||||
foreach(var h in hashBytes)
|
||||
{
|
||||
Assert.AreNotEqual(h, 0, "checksum mismatch");
|
||||
++count;
|
||||
}
|
||||
// The mask should have ff
|
||||
var hashMaskBytes = GetBytes(topicHashMask);
|
||||
count = 0;
|
||||
foreach (var h in hashMaskBytes)
|
||||
{
|
||||
Assert.AreEqual(h, 0xff, "mask mismatch");
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being +
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_DetectSingleLevelWildcard()
|
||||
{
|
||||
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/+";
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test long topic name with last level being #
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void Match_Hash_Test_LongTopic_DetectMultiLevelWildcard()
|
||||
{
|
||||
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/#";
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Match_Hash_Test_Search_NoWildcard()
|
||||
{
|
||||
var topics = await PrepareTopicHashSubscriptions(TopicHashSelector.NoWildcard);
|
||||
|
||||
// all match lookup
|
||||
var matchCount = CheckTopicSubscriptions(_clientSession, topics, "No Wildcard All Match");
|
||||
|
||||
Assert.AreEqual(topics.Count, matchCount, "Not all topics matched");
|
||||
|
||||
// no match lookup
|
||||
{
|
||||
{
|
||||
var topicsToFind = new List<string>();
|
||||
foreach (var t in topics)
|
||||
{
|
||||
topicsToFind.Add(t + "x");
|
||||
}
|
||||
|
||||
matchCount = CheckTopicSubscriptions(_clientSession, topicsToFind, "No Wildcard No Match (Append X)");
|
||||
|
||||
Assert.AreEqual(0, matchCount, "Topic match count not zero");
|
||||
}
|
||||
|
||||
{
|
||||
var topicsToFind = new List<string>();
|
||||
foreach (var t in topics)
|
||||
{
|
||||
// replace last letter with x
|
||||
topicsToFind.Add(t.Substring(0, t.Length - 1) + "x");
|
||||
}
|
||||
|
||||
matchCount = CheckTopicSubscriptions(_clientSession, topicsToFind, "No Wildcard No Match (Replace X)");
|
||||
|
||||
Assert.AreEqual(0, matchCount, "Topic match count not zero");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Match_Hash_Test_Search_SingleWildcard()
|
||||
{
|
||||
var topics = await PrepareTopicHashSubscriptions(TopicHashSelector.SingleWildcard);
|
||||
var matchCount = CheckTopicSubscriptions(_clientSession, topics, "Single Wildcard");
|
||||
// Should match all topics
|
||||
Assert.AreEqual(topics.Count, matchCount, "Topics not matched");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Match_Hash_Test_Search_MultiWildcard()
|
||||
{
|
||||
var topics = await PrepareTopicHashSubscriptions(TopicHashSelector.MultiWildcard);
|
||||
var matchCount = CheckTopicSubscriptions(_clientSession, topics, "Multi Wildcard");
|
||||
// Should match all topics
|
||||
Assert.AreEqual(topics.Count, matchCount, "Topics not matched");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Even fairly regularly named topics as generated by the topic generator should result in shallow hash buckets
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[TestMethod]
|
||||
public void Check_Hash_Bucket_Depth()
|
||||
{
|
||||
Dictionary<string, List<string>> topicsByPublisher;
|
||||
Dictionary<string, List<string>> singleWildcardTopicsByPublisher;
|
||||
Dictionary<string, List<string>> multiWildcardTopicsByPublisher;
|
||||
|
||||
const int NumPublishers = 5000;
|
||||
const int NumTopicsPerPublisher = 10;
|
||||
|
||||
TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
|
||||
|
||||
// There will be many 'similar' topics ending with, i.e. "sensor100", "sensor101", ...
|
||||
// Hash bucket depths should remain low.
|
||||
var bucketDepths = new Dictionary<UInt64, int>();
|
||||
int maxBucketDepth = 0;
|
||||
UInt64 maxBucketDepthHash = 0;
|
||||
|
||||
var topicsByHash = new Dictionary<UInt64, List<string>>();
|
||||
|
||||
foreach (var t in topicsByPublisher)
|
||||
{
|
||||
var topics = t.Value;
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
UInt64 topicHash;
|
||||
UInt64 hashMask;
|
||||
bool hasWildcard;
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out hashMask, out hasWildcard);
|
||||
|
||||
bucketDepths.TryGetValue(topicHash, out var currentValue);
|
||||
++currentValue;
|
||||
bucketDepths[topicHash] = currentValue;
|
||||
|
||||
if (currentValue > maxBucketDepth)
|
||||
{
|
||||
maxBucketDepth = currentValue;
|
||||
maxBucketDepthHash = topicHash;
|
||||
}
|
||||
|
||||
if (!topicsByHash.TryGetValue(topicHash, out var topicList))
|
||||
{
|
||||
topicList = new List<string>();
|
||||
topicsByHash.Add(topicHash, topicList);
|
||||
}
|
||||
topicList.Add(topic);
|
||||
}
|
||||
}
|
||||
|
||||
var maxDepthTopics = topicsByHash[maxBucketDepthHash];
|
||||
|
||||
Console.Write("Max bucket depth is " + maxBucketDepth);
|
||||
|
||||
// for the test case the bucket depth should be less than 100
|
||||
Assert.IsTrue(maxBucketDepth < 100, "Unexpected high topic hash bucket depth");
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void Check_Selected_Topic_Hashes()
|
||||
{
|
||||
CheckTopicHash("client1/building1/level1/sensor1", 0x655D4AF100000000, 0xFFFFFFFFFFFFFFFF);
|
||||
CheckTopicHash("client1/building1/+/sensor1", 0x655D00F100000000, 0xFFFF00FFFFFFFFFF);
|
||||
CheckTopicHash("client1/+/level1/+", 0x65004A0000000000, 0xFF00FF00FFFFFFFF);
|
||||
CheckTopicHash("client1/building1/level1/#", 0x655D4A0000000000, 0xFFFFFF0000000000);
|
||||
CheckTopicHash("client1/+/level1/#", 0x65004A0000000000, 0xFF00FF0000000000);
|
||||
}
|
||||
|
||||
void CheckTopicHash(string topic, ulong expectedHash, ulong expectedHashMask)
|
||||
{
|
||||
ulong topicHash;
|
||||
ulong hashMask;
|
||||
bool hasWildcard;
|
||||
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out hashMask, out hasWildcard);
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Topic: " + topic);
|
||||
Console.WriteLine(string.Format("Hash: {0:X8}", topicHash));
|
||||
Console.WriteLine(string.Format("Hash Mask: {0:X8}", hashMask));
|
||||
|
||||
Assert.AreEqual(expectedHash, topicHash, "Topic hash not as expected. Has the hash function changed?");
|
||||
Assert.AreEqual(expectedHashMask, hashMask, "Topic hash mask not as expected");
|
||||
}
|
||||
|
||||
|
||||
async Task<List<string>> PrepareTopicHashSubscriptions(TopicHashSelector selector)
|
||||
{
|
||||
Dictionary<string, List<string>> topicsByPublisher;
|
||||
Dictionary<string, List<string>> singleWildcardTopicsByPublisher;
|
||||
Dictionary<string, List<string>> multiWildcardTopicsByPublisher;
|
||||
|
||||
const int NumPublishers = 1;
|
||||
const int NumTopicsPerPublisher = 10000;
|
||||
|
||||
TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
|
||||
|
||||
var topics = topicsByPublisher.FirstOrDefault().Value;
|
||||
var singleWildcardTopics = singleWildcardTopicsByPublisher.FirstOrDefault().Value;
|
||||
var multiWildcardTopics = multiWildcardTopicsByPublisher.FirstOrDefault().Value;
|
||||
|
||||
const string ClientId = "Client1";
|
||||
var logger = new Mockups.TestLogger();
|
||||
var serverOptions = new MQTTnet.Server.MqttServerOptions();
|
||||
var eventContainer = new MQTTnet.Server.MqttServerEventContainer();
|
||||
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
|
||||
var sessionManager = new MqttClientSessionsManager(serverOptions, retainedMessagesManager, eventContainer, logger);
|
||||
_clientSession = new MQTTnet.Server.MqttSession(
|
||||
ClientId,
|
||||
false,
|
||||
new Dictionary<object, object>(),
|
||||
serverOptions,
|
||||
eventContainer,
|
||||
retainedMessagesManager,
|
||||
sessionManager
|
||||
);
|
||||
|
||||
List<string> topicsToSubscribe;
|
||||
|
||||
switch (selector)
|
||||
{
|
||||
case TopicHashSelector.SingleWildcard:
|
||||
topicsToSubscribe = singleWildcardTopics;
|
||||
break;
|
||||
case TopicHashSelector.MultiWildcard:
|
||||
topicsToSubscribe = multiWildcardTopics;
|
||||
break;
|
||||
default:
|
||||
topicsToSubscribe = topics;
|
||||
break;
|
||||
}
|
||||
foreach (var t in topicsToSubscribe)
|
||||
{
|
||||
var subPacket = new Packets.MqttSubscribePacket();
|
||||
var filter = new Packets.MqttTopicFilter();
|
||||
filter.Topic = t;
|
||||
subPacket.TopicFilters.Add(filter);
|
||||
await _clientSession.SubscriptionsManager.Subscribe(subPacket, default(CancellationToken));
|
||||
}
|
||||
|
||||
return topics;
|
||||
}
|
||||
|
||||
int CheckTopicSubscriptions(MQTTnet.Server.MqttSession clientSession, List<string> topicsToFind, string subject)
|
||||
{
|
||||
var matchCount = 0;
|
||||
|
||||
{
|
||||
int resultCount = 0;
|
||||
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
var countUp = 0;
|
||||
var countDown = topicsToFind.Count - 1;
|
||||
for (; countUp < topicsToFind.Count; ++countUp, --countDown)
|
||||
{
|
||||
var topicToFind = topicsToFind[countDown];
|
||||
|
||||
UInt64 topicHash;
|
||||
UInt64 hashMask;
|
||||
bool hasWildcard;
|
||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash((string)topicToFind, out topicHash, out hashMask, out hasWildcard);
|
||||
|
||||
var result = clientSession.SubscriptionsManager.CheckSubscriptions((string)topicToFind, topicHash, MqttQualityOfServiceLevel.AtMostOnce, "OtherClient");
|
||||
if (result.IsSubscribed)
|
||||
{
|
||||
++matchCount;
|
||||
}
|
||||
++resultCount;
|
||||
}
|
||||
|
||||
stopWatch.Stop();
|
||||
|
||||
Console.Write("Match count: " + matchCount + "; ");
|
||||
|
||||
Console.WriteLine(subject + " lookup milliseconds: " + stopWatch.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
return matchCount;
|
||||
}
|
||||
|
||||
byte[] GetBytes(UInt64 value)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
// Ensure that highest byte comes first for comparison left to right
|
||||
if (BitConverter.IsLittleEndian)
|
||||
return bytes.Reverse().ToArray();
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Topic_Alias_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Topic_Alias_Supported()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.Build());
|
||||
|
||||
Assert.AreEqual(connectResult.TopicAliasMaximum, ushort.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_With_Topic_Alias()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receivedTopics = new List<string>();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(options => options.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
lock (receivedTopics)
|
||||
{
|
||||
receivedTopics.Add(e.ApplicationMessage.Topic);
|
||||
}
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync("#");
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient(options => options.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var message = new MqttApplicationMessage
|
||||
{
|
||||
Topic = "this_is_the_topic",
|
||||
TopicAlias = 22
|
||||
};
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
|
||||
message.Topic = null;
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
await c2.PublishAsync(message);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(3, receivedTopics.Count);
|
||||
CollectionAssert.AllItemsAreNotNull(receivedTopics);
|
||||
Assert.IsTrue(receivedTopics.All(t => t.Equals("this_is_the_topic")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public class Feature_Tests
|
||||
{
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_User_Properties()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var message = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("A")
|
||||
.WithUserProperty("x", "1")
|
||||
.WithUserProperty("y", "2")
|
||||
.WithUserProperty("z", "3")
|
||||
.WithUserProperty("z", "4"); // z is here two times to test list of items
|
||||
|
||||
await receiver.SubscribeAsync(new MqttClientSubscribeOptions
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilter { Topic = "#" }
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
|
||||
MqttApplicationMessage receivedMessage = null;
|
||||
receiver.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.IsNotNull(receivedMessage);
|
||||
Assert.AreEqual("A", receivedMessage.Topic);
|
||||
Assert.AreEqual(4, receivedMessage.UserProperties.Count);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Wildcard_Subscription_Available_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Wildcard_Subscription_Available_Tests_Supported_V3()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.IsTrue(connectResult.WildcardSubscriptionAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Wildcard_Subscription_Available_Tests_Supported_V5()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.IsTrue(connectResult.WildcardSubscriptionAvailable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user