Refactor session and connection handling in server. Fix QoS level 2 issues.

This commit is contained in:
Christian Kratky
2019-01-27 11:15:32 +01:00
parent ebf0e91ab8
commit 6a2bded181
72 changed files with 2919 additions and 2461 deletions
@@ -25,7 +25,7 @@ namespace MQTTnet.AspNetCore.Tests
pipe.Receive.Writer.Complete();
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(TimeSpan.FromSeconds(1), CancellationToken.None));
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(TimeSpan.Zero, CancellationToken.None));
}
[TestMethod]
@@ -41,7 +41,7 @@ namespace MQTTnet.AspNetCore.Tests
{
for (int i = 0; i < 100; i++)
{
await ctx.SendPacketAsync(new MqttPublishPacket(), CancellationToken.None).ConfigureAwait(false);
await ctx.SendPacketAsync(new MqttPublishPacket(), TimeSpan.Zero, CancellationToken.None).ConfigureAwait(false);
}
}));
@@ -66,7 +66,7 @@ namespace MQTTnet.Benchmarks
for (var i = 0; i < 10000; i++)
{
_channelAdapter.SendPacketAsync(_packet, CancellationToken.None).GetAwaiter().GetResult();
_channelAdapter.SendPacketAsync(_packet, TimeSpan.Zero, CancellationToken.None).GetAwaiter().GetResult();
}
_stream.Position = 0;
@@ -85,7 +85,7 @@ namespace MQTTnet.Benchmarks
throw new NotImplementedException();
}
public Task DisconnectAsync()
public Task DisconnectAsync(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
+7 -6
View File
@@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Exceptions;
using MQTTnet.Internal;
namespace MQTTnet.Tests
{
@@ -14,20 +15,20 @@ namespace MQTTnet.Tests
[TestMethod]
public async Task TimeoutAfter()
{
await MQTTnet.Internal.TaskExtensions.TimeoutAfterAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(500), ct), TimeSpan.FromMilliseconds(100), CancellationToken.None);
await MqttTaskTimeout.WaitAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(500), ct), TimeSpan.FromMilliseconds(100), CancellationToken.None);
}
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
[TestMethod]
public async Task TimeoutAfterWithResult()
{
await MQTTnet.Internal.TaskExtensions.TimeoutAfterAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(500), ct).ContinueWith(t => 5, ct), TimeSpan.FromMilliseconds(100), CancellationToken.None);
await MqttTaskTimeout.WaitAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(500), ct).ContinueWith(t => 5, ct), TimeSpan.FromMilliseconds(100), CancellationToken.None);
}
[TestMethod]
public async Task TimeoutAfterCompleteInTime()
{
var result = await MQTTnet.Internal.TaskExtensions.TimeoutAfterAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(100), ct).ContinueWith(t => 5, ct), TimeSpan.FromMilliseconds(500), CancellationToken.None);
var result = await MqttTaskTimeout.WaitAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(100), ct).ContinueWith(t => 5, ct), TimeSpan.FromMilliseconds(500), CancellationToken.None);
Assert.AreEqual(5, result);
}
@@ -36,7 +37,7 @@ namespace MQTTnet.Tests
{
try
{
await MQTTnet.Internal.TaskExtensions.TimeoutAfterAsync(ct => Task.Run(() =>
await MqttTaskTimeout.WaitAsync(ct => Task.Run(() =>
{
var iis = new int[0];
iis[1] = 0;
@@ -55,7 +56,7 @@ namespace MQTTnet.Tests
{
try
{
await MQTTnet.Internal.TaskExtensions.TimeoutAfterAsync(ct => Task.Run(() =>
await MqttTaskTimeout.WaitAsync(ct => Task.Run(() =>
{
var iis = new int[0];
iis[1] = 0;
@@ -76,7 +77,7 @@ namespace MQTTnet.Tests
var tasks = Enumerable.Range(0, 100000)
.Select(i =>
{
return MQTTnet.Internal.TaskExtensions.TimeoutAfterAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(1), ct), TimeSpan.FromMinutes(1), CancellationToken.None);
return MqttTaskTimeout.WaitAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(1), ct), TimeSpan.FromMinutes(1), CancellationToken.None);
});
await Task.WhenAll(tasks);
@@ -16,7 +16,7 @@ namespace MQTTnet.Tests.MQTTv5
public class Client_Tests
{
[TestMethod]
public async Task Client_Connect()
public async Task Connect()
{
var server = new MqttFactory().CreateMqttServer();
var client = new MqttFactory().CreateMqttClient();
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests.MQTTv5
}
[TestMethod]
public async Task Client_Connect_And_Disconnect()
public async Task Connect_And_Disconnect()
{
var server = new MqttFactory().CreateMqttServer();
var client = new MqttFactory().CreateMqttClient();
@@ -52,7 +52,7 @@ namespace MQTTnet.Tests.MQTTv5
}
[TestMethod]
public async Task Client_Subscribe()
public async Task Subscribe()
{
var server = new MqttFactory().CreateMqttServer();
var client = new MqttFactory().CreateMqttClient();
@@ -75,7 +75,7 @@ namespace MQTTnet.Tests.MQTTv5
}
[TestMethod]
public async Task Client_Unsubscribe()
public async Task Unsubscribe()
{
var server = new MqttFactory().CreateMqttServer();
var client = new MqttFactory().CreateMqttClient();
@@ -99,7 +99,7 @@ namespace MQTTnet.Tests.MQTTv5
}
[TestMethod]
public async Task Client_Publish_QoS0()
public async Task Publish_QoS_0()
{
var server = new MqttFactory().CreateMqttServer();
var client = new MqttFactory().CreateMqttClient();
@@ -121,7 +121,7 @@ namespace MQTTnet.Tests.MQTTv5
}
[TestMethod]
public async Task Client_Publish_QoS1()
public async Task Publish_QoS_1()
{
var server = new MqttFactory().CreateMqttServer();
var client = new MqttFactory().CreateMqttClient();
@@ -143,7 +143,7 @@ namespace MQTTnet.Tests.MQTTv5
}
[TestMethod]
public async Task Client_Publish_QoS2()
public async Task Publish_QoS_2()
{
var server = new MqttFactory().CreateMqttServer();
var client = new MqttFactory().CreateMqttClient();
@@ -165,7 +165,7 @@ namespace MQTTnet.Tests.MQTTv5
}
[TestMethod]
public async Task Client_Publish_With_Properties()
public async Task Publish_With_Properties()
{
var server = new MqttFactory().CreateMqttServer();
var client = new MqttFactory().CreateMqttClient();
@@ -7,9 +7,9 @@ using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Server;
namespace MQTTnet.Tests
namespace MQTTnet.Tests.Mockups
{
public class TestSetup : IDisposable
public class TestEnvironment : IDisposable
{
private readonly MqttFactory _mqttFactory = new MqttFactory();
private readonly List<IMqttClient> _clients = new List<IMqttClient>();
@@ -24,9 +24,12 @@ namespace MQTTnet.Tests
private IMqttServer _server;
public bool IgnoreClientLogErrors { get; set; }
public bool IgnoreServerLogErrors { get; set; }
public TestSetup()
public int ServerPort { get; set; } = 1888;
public TestEnvironment()
{
_serverLogger.LogMessagePublished += (s, e) =>
{
@@ -51,6 +54,11 @@ namespace MQTTnet.Tests
};
}
public IMqttClient CreateClient()
{
return _mqttFactory.CreateMqttClient(_clientLogger);
}
public Task<IMqttServer> StartServerAsync()
{
return StartServerAsync(new MqttServerOptionsBuilder());
@@ -64,7 +72,7 @@ namespace MQTTnet.Tests
}
_server = _mqttFactory.CreateMqttServer(_serverLogger);
await _server.StartAsync(options.WithDefaultEndpointPort(1888).Build());
await _server.StartAsync(options.WithDefaultEndpointPort(ServerPort).Build());
return _server;
}
@@ -77,7 +85,7 @@ namespace MQTTnet.Tests
public async Task<IMqttClient> ConnectClientAsync(MqttClientOptionsBuilder options)
{
var client = _mqttFactory.CreateMqttClient(_clientLogger);
await client.ConnectAsync(options.WithTcpServer("localhost", 1888).Build());
await client.ConnectAsync(options.WithTcpServer("localhost", ServerPort).Build());
_clients.Add(client);
return client;
@@ -106,17 +114,16 @@ namespace MQTTnet.Tests
{
foreach (var mqttClient in _clients)
{
mqttClient?.DisconnectAsync().GetAwaiter().GetResult();
mqttClient?.Dispose();
}
_server.StopAsync().GetAwaiter().GetResult();
_server?.StopAsync().GetAwaiter().GetResult();
ThrowIfLogErrors();
if (_exceptions.Any())
{
throw new Exception($"{_exceptions.Count} exceptions tracked.");
throw new Exception($"{_exceptions.Count} exceptions tracked.\r\n" + string.Join(Environment.NewLine, _exceptions));
}
}
@@ -6,7 +6,7 @@ using MQTTnet.Adapter;
using MQTTnet.Formatter;
using MQTTnet.Packets;
namespace MQTTnet.Tests
namespace MQTTnet.Tests.Mockups
{
public class TestMqttCommunicationAdapter : IMqttChannelAdapter
{
@@ -35,7 +35,7 @@ namespace MQTTnet.Tests
return Task.FromResult(0);
}
public Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
public Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
{
ThrowIfPartnerIsNull();
@@ -44,30 +44,11 @@ namespace MQTTnet.Tests
return Task.FromResult(0);
}
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
public Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
ThrowIfPartnerIsNull();
if (timeout > TimeSpan.Zero)
{
using (var timeoutCts = new CancellationTokenSource(timeout))
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken))
{
return await Task.Run(() =>
{
try
{
return _incomingPackets.Take(cts.Token);
}
catch
{
return null;
}
}, cts.Token);
}
}
return await Task.Run(() =>
return Task.Run(() =>
{
try
{
@@ -1,9 +1,8 @@
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
namespace MQTTnet.Tests
namespace MQTTnet.Tests.Mockups
{
public class TestMqttCommunicationAdapterFactory : IMqttClientAdapterFactory
{
@@ -7,7 +7,7 @@ using MQTTnet.Client.Options;
using MQTTnet.Diagnostics;
using MQTTnet.Server;
namespace MQTTnet.Tests
namespace MQTTnet.Tests.Mockups
{
public class TestMqttServerAdapter : IMqttServerAdapter
{
@@ -2,7 +2,7 @@
using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Tests
namespace MQTTnet.Tests.Mockups
{
public static class TestServerExtensions
{
@@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Server;
namespace MQTTnet.Tests.Mockups
{
public class TestServerStorage : IMqttServerStorage
{
public IList<MqttApplicationMessage> Messages = new List<MqttApplicationMessage>();
public Task SaveRetainedMessagesAsync(IList<MqttApplicationMessage> messages)
{
Messages = messages;
return Task.CompletedTask;
}
public Task<IList<MqttApplicationMessage>> LoadRetainedMessagesAsync()
{
return Task.FromResult(Messages);
}
}
}
+81 -112
View File
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Client;
@@ -11,50 +10,73 @@ using MQTTnet.Client.Receiving;
using MQTTnet.Exceptions;
using MQTTnet.Protocol;
using MQTTnet.Server;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests
{
[TestClass]
public class MqttClientTests
public class Client_Tests
{
[TestMethod]
public async Task Client_Disconnect_Exception()
public async Task Invalid_Connect_Throws_Exception()
{
var factory = new MqttFactory();
var client = factory.CreateMqttClient();
Exception ex = null;
client.Disconnected += (s, e) =>
using (var client = factory.CreateMqttClient())
{
ex = e.Exception;
};
try
{
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("wrong-server").Build());
try
{
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("wrong-server").Build());
Assert.Fail("Must fail!");
}
catch (Exception exception)
{
Assert.IsNotNull(exception);
Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
}
}
catch
{
}
Assert.IsNotNull(ex);
Assert.IsInstanceOfType(ex, typeof(MqttCommunicationException));
Assert.IsInstanceOfType(ex.InnerException, typeof(SocketException));
}
[TestMethod]
public async Task Client_Preserve_Message_Order()
public async Task Disconnect_Event_Contains_Exception()
{
var factory = new MqttFactory();
using (var client = factory.CreateMqttClient())
{
Exception ex = null;
client.Disconnected += (s, e) =>
{
ex = e.Exception;
};
try
{
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("wrong-server").Build());
}
catch
{
}
Assert.IsNotNull(ex);
Assert.IsInstanceOfType(ex, typeof(MqttCommunicationException));
Assert.IsInstanceOfType(ex.InnerException, typeof(SocketException));
}
}
[TestMethod]
public async Task Preserve_Message_Order()
{
// The messages are sent in reverse or to ensure that the delay in the handler
// needs longer for the first messages and later messages may be processed earlier (if there
// is an issue).
const int MessagesCount = 50;
using (var testSetup = new TestSetup())
using (var testEnvironment = new TestEnvironment())
{
await testSetup.StartServerAsync();
await testEnvironment.StartServerAsync();
var client1 = await testSetup.ConnectClientAsync();
var client1 = await testEnvironment.ConnectClientAsync();
await client1.SubscribeAsync("x");
var receivedValues = new List<int>();
@@ -72,7 +94,7 @@ namespace MQTTnet.Tests
client1.UseReceivedApplicationMessageHandler(Handler1);
var client2 = await testSetup.ConnectClientAsync();
var client2 = await testEnvironment.ConnectClientAsync();
for (var i = MessagesCount; i > 0; i--)
{
await client2.PublishAsync("x", i.ToString());
@@ -88,13 +110,13 @@ namespace MQTTnet.Tests
}
[TestMethod]
public async Task Client_Send_Reply_For_Any_Received_Message()
public async Task Send_Reply_For_Any_Received_Message()
{
using (var testSetup = new TestSetup())
using (var testEnvironment = new TestEnvironment())
{
await testSetup.StartServerAsync();
await testEnvironment.StartServerAsync();
var client1 = await testSetup.ConnectClientAsync();
var client1 = await testEnvironment.ConnectClientAsync();
await client1.SubscribeAsync("request/+");
async Task Handler1(MqttApplicationMessageHandlerContext context)
@@ -104,7 +126,7 @@ namespace MQTTnet.Tests
client1.UseReceivedApplicationMessageHandler(Handler1);
var client2 = await testSetup.ConnectClientAsync();
var client2 = await testEnvironment.ConnectClientAsync();
await client2.SubscribeAsync("reply/#");
var replies = new List<string>();
@@ -132,30 +154,26 @@ namespace MQTTnet.Tests
}
[TestMethod]
public async Task Client_Publish()
public async Task Publish_With_Correct_Retain_Flag()
{
var server = new MqttFactory().CreateMqttServer();
try
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync();
var receivedMessages = new List<MqttApplicationMessage>();
await server.StartAsync(new MqttServerOptions());
var client1 = new MqttFactory().CreateMqttClient();
client1.ApplicationMessageReceived += (_, e) =>
var client1 = await testEnvironment.ConnectClientAsync();
client1.UseReceivedApplicationMessageHandler(c =>
{
lock (receivedMessages)
{
receivedMessages.Add(e.ApplicationMessage);
receivedMessages.Add(c.ApplicationMessage);
}
};
});
await client1.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build());
await client1.SubscribeAsync("a");
var client2 = new MqttFactory().CreateMqttClient();
await client2.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build());
var client2 = await testEnvironment.ConnectClientAsync();
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithRetainFlag().Build();
await client2.PublishAsync(message);
@@ -164,26 +182,18 @@ namespace MQTTnet.Tests
Assert.AreEqual(1, receivedMessages.Count);
Assert.IsFalse(receivedMessages.First().Retain); // Must be false even if set above!
}
finally
{
await server.StopAsync();
}
}
[TestMethod]
public async Task Publish_Special_Content()
public async Task Subscribe_In_Callback_Events()
{
var factory = new MqttFactory();
var server = factory.CreateMqttServer();
var serverOptions = new MqttServerOptionsBuilder().Build();
var receivedMessages = new List<MqttApplicationMessage>();
var client = factory.CreateMqttClient();
try
using (var testEnvironment = new TestEnvironment())
{
await server.StartAsync(serverOptions);
await testEnvironment.StartServerAsync();
var receivedMessages = new List<MqttApplicationMessage>();
var client = testEnvironment.CreateClient();
client.Connected += async (s, e) =>
{
@@ -196,43 +206,37 @@ namespace MQTTnet.Tests
await client.PublishAsync(msg.Build());
};
client.ApplicationMessageReceived += (s, e) =>
client.UseReceivedApplicationMessageHandler(c =>
{
lock (receivedMessages)
{
receivedMessages.Add(e.ApplicationMessage);
receivedMessages.Add(c.ApplicationMessage);
}
};
});
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
await Task.Delay(500);
Assert.AreEqual(1, receivedMessages.Count);
Assert.AreEqual("DA|18RS00SC00XI0000RV00R100R200R300R400L100L200L300L400Y100Y200AC0102031800BELK0000BM0000|", receivedMessages.First().ConvertPayloadToString());
}
finally
{
await server.StopAsync();
}
}
[TestMethod]
public async Task Client_Exception_In_Application_Message_Handler()
public async Task Message_Send_Retry()
{
using (var testSetup = new TestSetup())
using (var testEnvironment = new TestEnvironment())
{
testSetup.IgnoreClientLogErrors = true;
testSetup.IgnoreServerLogErrors = true;
testEnvironment.IgnoreClientLogErrors = true;
testEnvironment.IgnoreServerLogErrors = true;
await testSetup.StartServerAsync(
await testEnvironment.StartServerAsync(
new MqttServerOptionsBuilder()
.WithPersistentSessions()
.WithDefaultCommunicationTimeout(TimeSpan.FromMilliseconds(50)));
var client1 = await testSetup.ConnectClientAsync(new MqttClientOptionsBuilder()
.WithCleanSession(false));
.WithDefaultCommunicationTimeout(TimeSpan.FromMilliseconds(250)));
var client1 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithCleanSession(false));
await client1.SubscribeAsync("x", MqttQualityOfServiceLevel.AtLeastOnce);
var retries = 0;
@@ -241,16 +245,16 @@ namespace MQTTnet.Tests
{
retries++;
await Task.Delay(50);
await Task.Delay(1000);
throw new Exception("Broken!");
}
client1.UseReceivedApplicationMessageHandler(Handler1);
var client2 = await testSetup.ConnectClientAsync();
var client2 = await testEnvironment.ConnectClientAsync();
await client2.PublishAsync("x");
await Task.Delay(1000);
await Task.Delay(3000);
// The server should disconnect clients which are not responding.
Assert.IsFalse(client1.IsConnected);
@@ -262,40 +266,5 @@ namespace MQTTnet.Tests
Assert.AreEqual(2, retries);
}
}
//#if DEBUG
// [TestMethod]
// public async Task Client_Cleanup_On_Authentification_Fails()
// {
// var channel = new TestMqttCommunicationAdapter();
// var channel2 = new TestMqttCommunicationAdapter();
// channel.Partner = channel2;
// channel2.Partner = channel;
// Task.Run(async () => {
// var connect = await channel2.ReceivePacketAsync(TimeSpan.Zero, CancellationToken.None);
// await channel2.SendPacketAsync(new MqttConnAckPacket
// {
// ConnectReturnCode = Protocol.MqttConnectReturnCode.ConnectionRefusedNotAuthorized
// }, CancellationToken.None);
// });
// var fake = new TestMqttCommunicationAdapterFactory(channel);
// var client = new MqttClient(fake, new MqttNetLogger());
// try
// {
// await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("any-server").Build());
// }
// catch (Exception ex)
// {
// Assert.IsInstanceOfType(ex, typeof(MqttConnectingFailedException));
// }
// Assert.IsTrue(client._packetReceiverTask == null || client._packetReceiverTask.IsCompleted, "receive loop not completed");
// Assert.IsTrue(client._keepAliveMessageSenderTask == null || client._keepAliveMessageSenderTask.IsCompleted, "keepalive loop not completed");
// }
//#endif
}
}
@@ -7,6 +7,7 @@ using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Packets;
using MQTTnet.Server;
using MQTTnet.Server.Status;
namespace MQTTnet.Tests
{
@@ -44,9 +45,9 @@ namespace MQTTnet.Tests
// Simulate traffic.
Thread.Sleep(1000); // Internally the keep alive timeout is multiplied with 1.5 as per protocol specification.
monitor.PacketReceived(new MqttPublishPacket());
monitor.PacketReceived();
Thread.Sleep(1000);
monitor.PacketReceived(new MqttPublishPacket());
monitor.PacketReceived();
Thread.Sleep(1000);
Assert.AreEqual(0, clientSession.StopCalledCount);
@@ -62,12 +63,12 @@ namespace MQTTnet.Tests
public int StopCalledCount { get; private set; }
public void FillStatus(MqttClientSessionStatus status)
public void FillStatus(MqttClientStatus status)
{
throw new NotSupportedException();
}
public void EnqueueApplicationMessage(MqttClientSession senderClientSession, MqttApplicationMessage applicationMessage)
public void EnqueueApplicationMessage(MqttClientConnection senderClientSession, MqttApplicationMessage applicationMessage)
{
throw new NotSupportedException();
}
@@ -82,7 +83,7 @@ namespace MQTTnet.Tests
throw new NotSupportedException();
}
public Task StopAsync(MqttClientDisconnectType disconnectType)
public Task StopAsync()
{
StopCalledCount++;
return Task.FromResult(0);
File diff suppressed because it is too large Load Diff
@@ -11,7 +11,7 @@ namespace MQTTnet.Tests
[TestMethod]
public void MqttSubscriptionsManager_SubscribeSingleSuccess()
{
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher());
var sm = new MqttClientSubscriptionsManager("", new MqttServerEventDispatcher(), new MqttServerOptions());
var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new TopicFilterBuilder().WithTopic("A/B/C").Build());
@@ -26,7 +26,7 @@ namespace MQTTnet.Tests
[TestMethod]
public void MqttSubscriptionsManager_SubscribeDifferentQoSSuccess()
{
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher());
var sm = new MqttClientSubscriptionsManager("", new MqttServerEventDispatcher(), new MqttServerOptions());
var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new TopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
@@ -41,7 +41,7 @@ namespace MQTTnet.Tests
[TestMethod]
public void MqttSubscriptionsManager_SubscribeTwoTimesSuccess()
{
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher());
var sm = new MqttClientSubscriptionsManager("", new MqttServerEventDispatcher(), new MqttServerOptions());
var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new TopicFilter { Topic = "#", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
@@ -57,7 +57,7 @@ namespace MQTTnet.Tests
[TestMethod]
public void MqttSubscriptionsManager_SubscribeSingleNoSuccess()
{
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher());
var sm = new MqttClientSubscriptionsManager("", new MqttServerEventDispatcher(), new MqttServerOptions());
var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new TopicFilterBuilder().WithTopic("A/B/C").Build());
@@ -70,7 +70,7 @@ namespace MQTTnet.Tests
[TestMethod]
public void MqttSubscriptionsManager_SubscribeAndUnsubscribeSingle()
{
var sm = new MqttClientSubscriptionsManager("", new MqttServerOptions(), new MqttServerEventDispatcher());
var sm = new MqttClientSubscriptionsManager("", new MqttServerEventDispatcher(), new MqttServerOptions());
var sp = new MqttSubscribePacket();
sp.TopicFilters.Add(new TopicFilterBuilder().WithTopic("A/B/C").Build());
@@ -81,7 +81,7 @@ namespace MQTTnet.Tests
var up = new MqttUnsubscribePacket();
up.TopicFilters.Add("A/B/C");
sm.Unsubscribe(up);
sm.UnsubscribeAsync(up);
Assert.IsFalse(sm.CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce).IsSubscribed);
}
@@ -0,0 +1,159 @@
using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Threading.Tasks;
using MQTTnet.Client.Options;
using MQTTnet.Tests.Mockups;
using MQTTnet.Client;
using MQTTnet.Protocol;
using MQTTnet.Server;
namespace MQTTnet.Tests
{
[TestClass]
public class Server_Status_Tests
{
[TestMethod]
public async Task Show_Client_And_Session_Statistics()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync();
var c1 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("client1"));
var c2 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("client2"));
await Task.Delay(500);
var clientStatus = await server.GetClientStatusAsync();
var sessionStatus = await server.GetSessionStatusAsync();
Assert.AreEqual(2, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
Assert.IsTrue(clientStatus.Any(s => s.ClientId == "client1"));
Assert.IsTrue(clientStatus.Any(s => s.ClientId == "client2"));
await c1.DisconnectAsync();
await c2.DisconnectAsync();
await Task.Delay(500);
clientStatus = await server.GetClientStatusAsync();
sessionStatus = await server.GetSessionStatusAsync();
Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(0, sessionStatus.Count);
}
}
[TestMethod]
public async Task Disconnect_Client()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync();
var c1 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("client1"));
await Task.Delay(500);
var clientStatus = await server.GetClientStatusAsync();
Assert.AreEqual(1, clientStatus.Count);
Assert.IsTrue(clientStatus.Any(s => s.ClientId == "client1"));
await clientStatus.First().DisconnectAsync();
await Task.Delay(500);
Assert.IsFalse(c1.IsConnected);
clientStatus = await server.GetClientStatusAsync();
Assert.AreEqual(0, clientStatus.Count);
}
}
[TestMethod]
public async Task Keep_Persistent_Session()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithPersistentSessions());
var c1 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("client1"));
var c2 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("client2"));
await c1.DisconnectAsync();
await Task.Delay(500);
var clientStatus = await server.GetClientStatusAsync();
var sessionStatus = await server.GetSessionStatusAsync();
Assert.AreEqual(1, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
await c2.DisconnectAsync();
await Task.Delay(500);
clientStatus = await server.GetClientStatusAsync();
sessionStatus = await server.GetSessionStatusAsync();
Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
}
}
[TestMethod]
public async Task Track_Sent_Application_Messages()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithPersistentSessions());
var c1 = await testEnvironment.ConnectClientAsync();
for (var i = 1; i < 25; i++)
{
await c1.PublishAsync("a");
await Task.Delay(50);
var clientStatus = await server.GetClientStatusAsync();
Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount);
Assert.AreEqual(0, clientStatus.First().ReceivedApplicationMessagesCount);
}
}
}
[TestMethod]
public async Task Track_Sent_Packets()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithPersistentSessions());
var c1 = await testEnvironment.ConnectClientAsync(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.PublishAsync("a", string.Empty, MqttQualityOfServiceLevel.AtLeastOnce);
await Task.Delay(50);
var clientStatus = await server.GetClientStatusAsync();
Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount, "SAMC invalid!");
Assert.AreEqual(i, clientStatus.First().SentPacketsCount, "SPC invalid!");
// +1 because ConnACK package is already counted.
Assert.AreEqual(i + 1, clientStatus.First().ReceivedPacketsCount, "RPC invalid!");
}
}
}
}
}
+970
View File
@@ -0,0 +1,970 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Adapter;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Options;
using MQTTnet.Protocol;
using MQTTnet.Server;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests
{
[TestClass]
public class Server_Tests
{
[TestMethod]
public async Task Publish_At_Most_Once_0x00()
{
await TestPublishAsync(
"A/B/C",
MqttQualityOfServiceLevel.AtMostOnce,
"A/B/C",
MqttQualityOfServiceLevel.AtMostOnce,
1);
}
[TestMethod]
public async Task Publish_At_Least_Once_0x01()
{
await TestPublishAsync(
"A/B/C",
MqttQualityOfServiceLevel.AtLeastOnce,
"A/B/C",
MqttQualityOfServiceLevel.AtLeastOnce,
1);
}
[TestMethod]
public async Task Publish_Exactly_Once_0x02()
{
await TestPublishAsync(
"A/B/C",
MqttQualityOfServiceLevel.ExactlyOnce,
"A/B/C",
MqttQualityOfServiceLevel.ExactlyOnce,
1);
}
[TestMethod]
public async Task Will_Message_Do_Not_Send()
{
using (var testEnvironment = new TestEnvironment())
{
var receivedMessagesCount = 0;
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder());
var willMessage = new MqttApplicationMessageBuilder().WithTopic("My/last/will").WithAtMostOnceQoS().Build();
var clientOptions = new MqttClientOptionsBuilder().WithWillMessage(willMessage);
var c1 = await testEnvironment.ConnectClientAsync();
c1.UseReceivedApplicationMessageHandler(c => Interlocked.Increment(ref receivedMessagesCount));
await c1.SubscribeAsync(new TopicFilterBuilder().WithTopic("#").Build());
var c2 = await testEnvironment.ConnectClientAsync(clientOptions);
await c2.DisconnectAsync().ConfigureAwait(false);
await Task.Delay(1000);
Assert.AreEqual(0, receivedMessagesCount);
}
}
[TestMethod]
public async Task Will_Message_Send()
{
using (var testEnvironment = new TestEnvironment())
{
var receivedMessagesCount = 0;
await testEnvironment.StartServerAsync();
var willMessage = new MqttApplicationMessageBuilder().WithTopic("My/last/will").WithAtMostOnceQoS().Build();
var clientOptions = new MqttClientOptionsBuilder().WithWillMessage(willMessage);
var c1 = await testEnvironment.ConnectClientAsync();
c1.UseReceivedApplicationMessageHandler(c => Interlocked.Increment(ref receivedMessagesCount));
await c1.SubscribeAsync(new TopicFilterBuilder().WithTopic("#").Build());
var c2 = await testEnvironment.ConnectClientAsync(clientOptions);
c2.Dispose(); // Dispose will not send a DISCONNECT pattern first so the will message must be sent.
await Task.Delay(1000);
Assert.AreEqual(1, receivedMessagesCount);
}
}
[TestMethod]
public async Task Subscribe_Unsubscribe()
{
using (var testEnvironment = new TestEnvironment())
{
var receivedMessagesCount = 0;
var server = await testEnvironment.StartServerAsync();
var c1 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("c1"));
c1.UseReceivedApplicationMessageHandler(c => Interlocked.Increment(ref receivedMessagesCount));
var c2 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("c2"));
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build();
await c2.PublishAsync(message);
await Task.Delay(500);
Assert.AreEqual(0, receivedMessagesCount);
var subscribeEventCalled = false;
server.ClientSubscribedTopic += (_, e) =>
{
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == "c1";
};
await c1.SubscribeAsync(new TopicFilter { 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.ClientUnsubscribedTopic += (_, e) =>
{
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == "c1";
};
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 Publish_From_Server()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync();
var receivedMessagesCount = 0;
var client = await testEnvironment.ConnectClientAsync();
client.UseReceivedApplicationMessageHandler(c => Interlocked.Increment(ref receivedMessagesCount));
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build();
await client.SubscribeAsync(new TopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
await server.PublishAsync(message);
await Task.Delay(1000);
Assert.AreEqual(1, receivedMessagesCount);
}
}
[TestMethod]
public async Task Publish_Multiple_Clients()
{
var receivedMessagesCount = 0;
var locked = new object();
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync();
var c1 = await testEnvironment.ConnectClientAsync();
var c2 = await testEnvironment.ConnectClientAsync();
c1.UseReceivedApplicationMessageHandler(c =>
{
lock (locked)
{
receivedMessagesCount++;
}
});
c2.UseReceivedApplicationMessageHandler(c =>
{
lock (locked)
{
receivedMessagesCount++;
}
});
await c1.SubscribeAsync(new TopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
await c2.SubscribeAsync(new TopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build();
for (var i = 0; i < 1000; i++)
{
await c1.PublishAsync(message);
}
await Task.Delay(500);
Assert.AreEqual(2000, receivedMessagesCount);
}
}
[TestMethod]
public async Task Session_Takeover()
{
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync();
var options = new MqttClientOptionsBuilder()
.WithCleanSession(false)
.WithClientId("a");
var client1 = await testEnvironment.ConnectClientAsync(options);
await Task.Delay(500);
var client2 = await testEnvironment.ConnectClientAsync(options);
await Task.Delay(500);
Assert.IsFalse(client1.IsConnected);
Assert.IsTrue(client2.IsConnected);
}
}
[TestMethod]
public async Task No_Messages_If_No_Subscription()
{
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync();
var client = await testEnvironment.ConnectClientAsync();
var receivedMessages = new List<MqttApplicationMessage>();
client.Connected += async (s, e) =>
{
await client.PublishAsync("Connected");
};
client.UseReceivedApplicationMessageHandler(c =>
{
lock (receivedMessages)
{
receivedMessages.Add(c.ApplicationMessage);
}
});
await Task.Delay(500);
await client.PublishAsync("Hello");
await Task.Delay(500);
Assert.AreEqual(0, receivedMessages.Count);
}
}
[TestMethod]
public async Task Set_Subscription_At_Server()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync();
server.ClientConnected += async (s, e) =>
{
await server.SubscribeAsync(e.ClientId, "topic1");
};
var client = await testEnvironment.ConnectClientAsync();
var receivedMessages = new List<MqttApplicationMessage>();
client.UseReceivedApplicationMessageHandler(c =>
{
lock (receivedMessages)
{
receivedMessages.Add(c.ApplicationMessage);
}
});
await Task.Delay(500);
await client.PublishAsync("Hello");
await Task.Delay(100);
Assert.AreEqual(0, receivedMessages.Count);
await client.PublishAsync("topic1");
await Task.Delay(100);
Assert.AreEqual(1, receivedMessages.Count);
}
}
[TestMethod]
public async Task Shutdown_Disconnects_Clients_Gracefully()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder());
var disconnectCalled = 0;
var c1 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder());
c1.Disconnected += (sender, args) => disconnectCalled++;
await Task.Delay(100);
await server.StopAsync();
await Task.Delay(100);
Assert.AreEqual(1, disconnectCalled);
}
}
[TestMethod]
public async Task Handle_Clean_Disconnect()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder());
var clientConnectedCalled = 0;
var clientDisconnectedCalled = 0;
server.ClientConnected += (_, __) => Interlocked.Increment(ref clientConnectedCalled);
server.ClientDisconnected += (_, __) => Interlocked.Increment(ref clientDisconnectedCalled);
var c1 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder());
Assert.AreEqual(1, clientConnectedCalled);
Assert.AreEqual(0, clientDisconnectedCalled);
await Task.Delay(500);
await c1.DisconnectAsync();
await Task.Delay(500);
Assert.AreEqual(1, clientConnectedCalled);
Assert.AreEqual(1, clientDisconnectedCalled);
}
}
[TestMethod]
public async Task Client_Disconnect_Without_Errors()
{
using (var testEnvironment = new TestEnvironment())
{
bool clientWasConnected;
var server = await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder());
try
{
var client = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder());
clientWasConnected = true;
await client.DisconnectAsync();
await Task.Delay(500);
}
finally
{
await server.StopAsync();
}
Assert.IsTrue(clientWasConnected);
testEnvironment.ThrowIfLogErrors();
}
}
[TestMethod]
public async Task Handle_Lots_Of_Parallel_Retained_Messages()
{
const int ClientCount = 50;
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync();
var tasks = new List<Task>();
for (var i = 0; i < ClientCount; i++)
{
var i2 = i;
var testEnvironment2 = testEnvironment;
tasks.Add(Task.Run(async () =>
{
try
{
using (var client = await testEnvironment2.ConnectClientAsync())
{
// Clear retained message.
await client.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("r" + i2)
.WithPayload(new byte[0]).WithRetainFlag().Build());
// Set retained message.
await client.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("r" + i2)
.WithPayload("value").WithRetainFlag().Build());
await client.DisconnectAsync();
}
}
catch (Exception exception)
{
testEnvironment2.TrackException(exception);
}
}));
}
await Task.WhenAll(tasks);
await Task.Delay(1000);
var retainedMessages = await server.GetRetainedMessagesAsync();
Assert.AreEqual(ClientCount, retainedMessages.Count);
for (var i = 0; i < ClientCount; i++)
{
Assert.IsTrue(retainedMessages.Any(m => m.Topic == "r" + i));
}
}
}
[TestMethod]
public async Task Retained_Messages_Flow()
{
using (var testEnvironment = new TestEnvironment())
{
var retainedMessage = new MqttApplicationMessageBuilder().WithTopic("r").WithPayload("r").WithRetainFlag().Build();
await testEnvironment.StartServerAsync();
var c1 = await testEnvironment.ConnectClientAsync();
var receivedMessages = 0;
var c2 = await testEnvironment.ConnectClientAsync();
c2.UseReceivedApplicationMessageHandler(c =>
{
Interlocked.Increment(ref receivedMessages);
});
await c1.PublishAsync(retainedMessage);
await c1.DisconnectAsync();
await Task.Delay(500);
for (var i = 0; i < 5; i++)
{
await c2.UnsubscribeAsync("r");
await Task.Delay(100);
Assert.AreEqual(i, receivedMessages);
await c2.SubscribeAsync("r");
await Task.Delay(100);
Assert.AreEqual(i + 1, receivedMessages);
}
await c2.DisconnectAsync();
}
}
[TestMethod]
public async Task Receive_No_Retained_Message_After_Subscribe()
{
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync();
var c1 = await testEnvironment.ConnectClientAsync();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await c1.DisconnectAsync();
var receivedMessagesCount = 0;
var c2 = await testEnvironment.ConnectClientAsync();
c2.UseReceivedApplicationMessageHandler(c => Interlocked.Increment(ref receivedMessagesCount));
await c2.SubscribeAsync(new TopicFilterBuilder().WithTopic("retained_other").Build());
await Task.Delay(500);
Assert.AreEqual(0, receivedMessagesCount);
}
}
[TestMethod]
public async Task Receive_Retained_Message_After_Subscribe()
{
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync();
var c1 = await testEnvironment.ConnectClientAsync();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await c1.DisconnectAsync();
var receivedMessages = new List<MqttApplicationMessage>();
var c2 = await testEnvironment.ConnectClientAsync();
c2.UseReceivedApplicationMessageHandler(c =>
{
lock (receivedMessages)
{
receivedMessages.Add(c.ApplicationMessage);
}
});
await c2.SubscribeAsync(new TopicFilterBuilder().WithTopic("retained").Build());
await Task.Delay(500);
Assert.AreEqual(1, receivedMessages.Count);
Assert.IsTrue(receivedMessages.First().Retain);
}
}
[TestMethod]
public async Task Clear_Retained_Message()
{
using (var testEnvironment = new TestEnvironment())
{
var receivedMessagesCount = 0;
await testEnvironment.StartServerAsync();
var c1 = await testEnvironment.ConnectClientAsync();
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.ConnectClientAsync();
c2.UseReceivedApplicationMessageHandler(c => Interlocked.Increment(ref receivedMessagesCount));
await Task.Delay(200);
await c2.SubscribeAsync(new TopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
await Task.Delay(500);
Assert.AreEqual(0, receivedMessagesCount);
}
}
[TestMethod]
public async Task Persist_Retained_Message()
{
var serverStorage = new TestServerStorage();
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithStorage(serverStorage));
var c1 = await testEnvironment.ConnectClientAsync();
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
await Task.Delay(500);
Assert.AreEqual(1, serverStorage.Messages.Count);
}
}
[TestMethod]
public async Task Intercept_Message()
{
void Interceptor(MqttApplicationMessageInterceptorContext context)
{
context.ApplicationMessage.Payload = Encoding.ASCII.GetBytes("extended");
}
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithApplicationMessageInterceptor(Interceptor));
var c1 = await testEnvironment.ConnectClientAsync();
var c2 = await testEnvironment.ConnectClientAsync();
await c2.SubscribeAsync(new TopicFilterBuilder().WithTopic("test").Build());
var isIntercepted = false;
c2.UseReceivedApplicationMessageHandler(c =>
{
isIntercepted = string.Compare("extended", Encoding.UTF8.GetString(c.ApplicationMessage.Payload), StringComparison.Ordinal) == 0;
});
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("test").Build());
await c1.DisconnectAsync();
await Task.Delay(500);
Assert.IsTrue(isIntercepted);
}
}
[TestMethod]
public async Task Send_Long_Body()
{
using (var testEnvironment = new TestEnvironment())
{
const int PayloadSizeInMB = 30;
const int CharCount = PayloadSizeInMB * 1024 * 1024;
var longBody = new byte[CharCount];
byte @char = 32;
for (long i = 0; i < PayloadSizeInMB * 1024L * 1024L; i++)
{
longBody[i] = @char;
@char++;
if (@char > 126)
{
@char = 32;
}
}
byte[] receivedBody = null;
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder());
var client1 = await testEnvironment.ConnectClientAsync();
client1.UseReceivedApplicationMessageHandler(c =>
{
receivedBody = c.ApplicationMessage.Payload;
});
await client1.SubscribeAsync("string");
var client2 = await testEnvironment.ConnectClientAsync();
await client2.PublishAsync("string", longBody);
await Task.Delay(500);
Assert.IsTrue(longBody.SequenceEqual(receivedBody ?? new byte[0]));
}
}
[TestMethod]
public async Task Deny_Connection()
{
var serverOptions = new MqttServerOptionsBuilder().WithConnectionValidator(context =>
{
context.ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized;
});
using (var testEnvironment = new TestEnvironment())
{
testEnvironment.IgnoreClientLogErrors = true;
await testEnvironment.StartServerAsync(serverOptions);
try
{
await testEnvironment.ConnectClientAsync();
Assert.Fail("An exception should be raised.");
}
catch (Exception exception)
{
if (exception is MqttConnectingFailedException connectingFailedException)
{
Assert.AreEqual(MqttClientConnectResultCode.NotAuthorized, connectingFailedException.ResultCode);
}
else
{
Assert.Fail("Wrong exception.");
}
}
}
}
[TestMethod]
public async Task Same_Client_Id_Connect_Disconnect_Event_Order()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder());
var events = new List<string>();
server.ClientConnected += (_, __) =>
{
lock (events)
{
events.Add("c");
}
};
server.ClientDisconnected += (_, __) =>
{
lock (events)
{
events.Add("d");
}
};
var clientOptions = new MqttClientOptionsBuilder()
.WithClientId("same_id");
// c
var c1 = await testEnvironment.ConnectClientAsync(clientOptions);
await Task.Delay(500);
var flow = string.Join(string.Empty, events);
Assert.AreEqual("c", flow);
// dc
var c2 = await testEnvironment.ConnectClientAsync(clientOptions);
await Task.Delay(500);
flow = string.Join(string.Empty, events);
Assert.AreEqual("cdc", flow);
// nothing
await c1.DisconnectAsync();
await Task.Delay(500);
// d
await c2.DisconnectAsync();
await Task.Delay(500);
await server.StopAsync();
flow = string.Join(string.Empty, events);
Assert.AreEqual("cdcd", flow);
}
}
[TestMethod]
public async Task Remove_Session()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder());
var clientOptions = new MqttClientOptionsBuilder();
var c1 = await testEnvironment.ConnectClientAsync(clientOptions);
await Task.Delay(500);
Assert.AreEqual(1, (await server.GetClientStatusAsync()).Count);
await c1.DisconnectAsync();
await Task.Delay(500);
Assert.AreEqual(0, (await server.GetClientStatusAsync()).Count);
}
}
[TestMethod]
public async Task Stop_And_Restart()
{
using (var testEnvironment = new TestEnvironment())
{
testEnvironment.IgnoreClientLogErrors = true;
var server = await testEnvironment.StartServerAsync();
await testEnvironment.ConnectClientAsync();
await server.StopAsync();
try
{
await testEnvironment.ConnectClientAsync();
Assert.Fail("Connecting should fail.");
}
catch (Exception)
{
}
await server.StartAsync(new MqttServerOptionsBuilder().WithDefaultEndpointPort(testEnvironment.ServerPort).Build());
await testEnvironment.ConnectClientAsync();
}
}
[TestMethod]
public async Task Close_Idle_Connection()
{
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await client.ConnectAsync("localhost", testEnvironment.ServerPort);
// 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 = new TestEnvironment())
{
await testEnvironment.StartServerAsync(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 Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await client.ConnectAsync("localhost", testEnvironment.ServerPort);
await client.SendAsync(Encoding.UTF8.GetBytes("Garbage"), 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)
{
}
}
}
[TestMethod]
public async Task Do_Not_Send_Retained_Messages_For_Denied_Subscription()
{
using (var testEnvironment = new TestEnvironment())
{
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithSubscriptionInterceptor(c =>
{
// This should lead to no subscriptions for "n" at all. So also no sending of retained messages.
if (c.TopicFilter.Topic == "n")
{
c.AcceptSubscription = false;
}
}));
// Prepare some retained messages.
var client1 = await testEnvironment.ConnectClientAsync();
await client1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("y").WithPayload("x").WithRetainFlag().Build());
await client1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("n").WithPayload("x").WithRetainFlag().Build());
await client1.DisconnectAsync();
await Task.Delay(500);
// Subscribe to all retained message types.
// It is important to do this in a range of filters to ensure that a subscription is not "hidden".
var client2 = await testEnvironment.ConnectClientAsync();
var buffer = new StringBuilder();
client2.UseReceivedApplicationMessageHandler(c =>
{
lock (buffer)
{
buffer.Append(c.ApplicationMessage.Topic);
}
});
await client2.SubscribeAsync(new TopicFilter { Topic = "y" }, new TopicFilter { Topic = "n" });
await Task.Delay(500);
Assert.AreEqual("y", buffer.ToString());
}
}
[TestMethod]
public async Task Collect_Messages_In_Disconnected_Session()
{
using (var testEnvironment = new TestEnvironment())
{
var server = await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder().WithPersistentSessions());
// Create the session including the subscription.
var client1 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("a"));
await client1.SubscribeAsync("x");
await client1.DisconnectAsync();
await Task.Delay(500);
var clientStatus = await server.GetClientStatusAsync();
Assert.AreEqual(0, clientStatus.Count);
var client2 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("b"));
await client2.PublishAsync("x", "1");
await client2.PublishAsync("x", "2");
await client2.PublishAsync("x", "3");
await client2.DisconnectAsync();
await Task.Delay(500);
clientStatus = await server.GetClientStatusAsync();
var sessionStatus = await server.GetSessionStatusAsync();
Assert.AreEqual(0, clientStatus.Count);
Assert.AreEqual(2, sessionStatus.Count);
Assert.AreEqual(3, sessionStatus.First(s => s.ClientId == "a").PendingApplicationMessagesCount);
}
}
private static async Task TestPublishAsync(
string topic,
MqttQualityOfServiceLevel qualityOfServiceLevel,
string topicFilter,
MqttQualityOfServiceLevel filterQualityOfServiceLevel,
int expectedReceivedMessagesCount)
{
using (var testEnvironment = new TestEnvironment())
{
var receivedMessagesCount = 0;
await testEnvironment.StartServerAsync(new MqttServerOptionsBuilder());
var c1 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("receiver"));
c1.UseReceivedApplicationMessageHandler(c => Interlocked.Increment(ref receivedMessagesCount));
await c1.SubscribeAsync(new TopicFilterBuilder().WithTopic(topicFilter).WithQualityOfServiceLevel(filterQualityOfServiceLevel).Build());
var c2 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("sender"));
await c2.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(new byte[0]).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
await c2.DisconnectAsync().ConfigureAwait(false);
await Task.Delay(500);
await c1.UnsubscribeAsync(topicFilter);
await Task.Delay(500);
Assert.AreEqual(expectedReceivedMessagesCount, receivedMessagesCount);
}
}
}
}
@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:server="using:MQTTnet.Server"
xmlns:status="using:MQTTnet.Server.Status"
d:DesignHeight="800"
d:DesignWidth="800"
mc:Ignorable="d">
@@ -228,7 +229,7 @@
<ListView ItemsSource="{Binding}" x:Name="ListViewSessions">
<ListView.ItemTemplate>
<DataTemplate x:DataType="server:IMqttClientSessionStatus">
<DataTemplate x:DataType="status:IMqttClientStatus">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="TextBlock">
@@ -237,11 +238,10 @@
</StackPanel.Resources>
<TextBlock Text="{Binding ClientId}"></TextBlock>
<TextBlock Text="{Binding Endpoint}"></TextBlock>
<TextBlock Text="{Binding IsConnected}"></TextBlock>
<TextBlock Text="{Binding LastPacketReceived}"></TextBlock>
<TextBlock Text="{Binding LastNonKeepAlivePacketReceived}"></TextBlock>
<TextBlock Text="{Binding ProtocolVersion}"></TextBlock>
<TextBlock Text="{Binding PendingApplicationMessagesCount}"></TextBlock>
<TextBlock Text="{Binding Session}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
@@ -16,6 +16,7 @@ using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Protocol;
using MQTTnet.Server;
using MQTTnet.Server.Status;
using MqttClientConnectedEventArgs = MQTTnet.Client.Connecting.MqttClientConnectedEventArgs;
using MqttClientDisconnectedEventArgs = MQTTnet.Client.Disconnecting.MqttClientDisconnectedEventArgs;
@@ -24,7 +25,7 @@ namespace MQTTnet.TestApp.UniversalWindows
public sealed partial class MainPage
{
private readonly ConcurrentQueue<MqttNetLogMessage> _traceMessages = new ConcurrentQueue<MqttNetLogMessage>();
private readonly ObservableCollection<IMqttClientSessionStatus> _sessions = new ObservableCollection<IMqttClientSessionStatus>();
private readonly ObservableCollection<IMqttClientStatus> _sessions = new ObservableCollection<IMqttClientStatus>();
private IMqttClient _mqttClient;
private IManagedMqttClient _managedMqttClient;
@@ -450,7 +451,7 @@ namespace MQTTnet.TestApp.UniversalWindows
return;
}
var sessions = _mqttServer.GetClientSessionsStatusAsync().GetAwaiter().GetResult();
var sessions = _mqttServer.GetClientStatusAsync().GetAwaiter().GetResult();
_sessions.Clear();
foreach (var session in sessions)