Improve internal server connection management. (#1232)

This commit is contained in:
Christian
2021-09-08 18:01:56 +02:00
committed by GitHub
parent 9d9077f17c
commit cd8940f9b7
60 changed files with 980 additions and 589 deletions
@@ -2,6 +2,7 @@
using BenchmarkDotNet.Jobs;
using MQTTnet.Server;
using System;
using MQTTnet.Server.Internal;
namespace MQTTnet.Benchmarks
{
+22
View File
@@ -0,0 +1,22 @@
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests
{
public abstract class BaseTestClass
{
public TestContext TestContext { get; set; }
protected TestEnvironment CreateTestEnvironment()
{
return new TestEnvironment(TestContext);
}
protected Task LongDelay()
{
return Task.Delay(TimeSpan.FromSeconds(1));
}
}
}
@@ -16,7 +16,7 @@ using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests.MQTTv5
{
[TestClass]
public class Client_Tests
public sealed class Client_Tests
{
public TestContext TestContext { get; set; }
@@ -60,6 +60,7 @@ namespace MQTTnet.Tests.MQTTv5
Assert.AreEqual(2, receivedMessage.UserProperties.Count);
}
}
[TestMethod]
public async Task Connect_With_AssignedClientId()
{
@@ -116,7 +117,6 @@ namespace MQTTnet.Tests.MQTTv5
Assert.AreEqual("test123", serverConnectedClientId);
Assert.AreEqual("test123", serverDisconnectedClientId);
Assert.AreEqual("test123", clientAssignedClientId);
}
}
@@ -165,7 +165,6 @@ namespace MQTTnet.Tests.MQTTv5
Assert.AreEqual(1, result.Items.Count);
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS1, result.Items[0].ResultCode);
}
}
@@ -172,21 +172,25 @@ namespace MQTTnet.Tests.Mockups
return StartServerAsync(new MqttServerOptionsBuilder());
}
public async Task<IMqttServer> StartServerAsync(MqttServerOptionsBuilder options)
public IMqttServer CreateServer()
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (Server != null)
{
throw new InvalidOperationException("Server already started.");
}
Server = new TestServerWrapper(_mqttFactory.CreateMqttServer(ServerLogger), TestContext, this);
return Server;
}
public async Task<IMqttServer> StartServerAsync(MqttServerOptionsBuilder options)
{
CreateServer();
options.WithDefaultEndpointPort(ServerPort);
options.WithMaxPendingMessagesPerClient(int.MaxValue);
await Server.StartAsync(options.Build()).ConfigureAwait(false);
await Server.StartAsync(options.Build());
return Server;
}
+1 -1
View File
@@ -50,7 +50,7 @@ namespace MQTTnet.Tests
Topic = "Test"
}, CancellationToken.None);
await Task.Delay(1000);
await Task.Delay(500);
// This will simulate a device which closes the connection directly
// after sending the data so do delay is added between send and dispose!
@@ -5,6 +5,7 @@ using MQTTnet.Server;
using MQTTnet.Tests.Mockups;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using MQTTnet.Server.Internal;
namespace MQTTnet.Tests
{
@@ -0,0 +1,186 @@
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Client;
using MQTTnet.Client.Receiving;
using MQTTnet.Formatter;
using MQTTnet.Protocol;
using MQTTnet.Server;
namespace MQTTnet.Tests
{
[TestClass]
public sealed class Server_Events_Tests : BaseTestClass
{
[TestMethod]
public async Task Fire_Client_Connected_Event()
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServerAsync();
MqttServerClientConnectedEventArgs eventArgs = null;
server.ClientConnectedHandler = new MqttServerClientConnectedHandlerDelegate(e =>
{
eventArgs = e;
});
await testEnvironment.ConnectClientAsync(o => o.WithCredentials("TheUser"));
await LongDelay();
Assert.IsNotNull(eventArgs);
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Connected_Event)));
Assert.IsTrue(eventArgs.Endpoint.Contains("::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.StartServerAsync();
MqttServerClientDisconnectedEventArgs eventArgs = null;
server.ClientDisconnectedHandler = new MqttServerClientDisconnectedHandlerDelegate(e =>
{
eventArgs = e;
});
var client = await testEnvironment.ConnectClientAsync(o => o.WithCredentials("TheUser"));
await client.DisconnectAsync();
await LongDelay();
Assert.IsNotNull(eventArgs);
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Disconnected_Event)));
Assert.IsTrue(eventArgs.Endpoint.Contains("::1"));
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
}
}
[TestMethod]
public async Task Fire_Client_Subscribed_Event()
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServerAsync();
MqttServerClientSubscribedTopicEventArgs eventArgs = null;
server.ClientSubscribedTopicHandler = new MqttServerClientSubscribedTopicHandlerDelegate(e =>
{
eventArgs = e;
});
var client = await testEnvironment.ConnectClientAsync();
await client.SubscribeAsync("The/Topic", MqttQualityOfServiceLevel.AtLeastOnce);
await LongDelay();
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.StartServerAsync();
MqttServerClientUnsubscribedTopicEventArgs eventArgs = null;
server.ClientUnsubscribedTopicHandler = new MqttServerClientUnsubscribedTopicHandlerDelegate(e =>
{
eventArgs = e;
});
var client = await testEnvironment.ConnectClientAsync();
await client.UnsubscribeAsync("The/Topic");
await LongDelay();
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.StartServerAsync();
MqttApplicationMessageReceivedEventArgs eventArgs = null;
server.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(e =>
{
eventArgs = e;
});
var client = await testEnvironment.ConnectClientAsync();
await client.PublishAsync("The_Topic", "The_Payload");
await LongDelay();
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();
EventArgs eventArgs = null;
server.StartedHandler = new MqttServerStartedHandlerDelegate(e =>
{
eventArgs = e;
});
await server.StartAsync(new MqttServerOptionsBuilder().Build());
await LongDelay();
Assert.IsNotNull(eventArgs);
}
}
[TestMethod]
public async Task Fire_Stopped_Event()
{
using (var testEnvironment = CreateTestEnvironment())
{
var server = await testEnvironment.StartServerAsync();
EventArgs eventArgs = null;
server.StoppedHandler = new MqttServerStoppedHandlerDelegate(e =>
{
eventArgs = e;
});
await server.StopAsync();
await LongDelay();
Assert.IsNotNull(eventArgs);
}
}
}
}
@@ -10,10 +10,8 @@ using MQTTnet.Server;
namespace MQTTnet.Tests
{
[TestClass]
public class Server_Status_Tests
public sealed class Server_Status_Tests : BaseTestClass
{
public TestContext TestContext { get; set; }
[TestMethod]
public async Task Show_Client_And_Session_Statistics()
{
@@ -143,7 +141,7 @@ namespace MQTTnet.Tests
// 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);
await Task.Delay(250);
var clientStatus = await server.GetClientStatusAsync();
+42 -5
View File
@@ -147,7 +147,7 @@ namespace MQTTnet.Tests
}
[TestMethod]
public async Task Will_Message_Do_Not_Send()
public async Task Will_Message_Do_Not_Send_On_Clean_Disconnect()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
@@ -155,7 +155,7 @@ namespace MQTTnet.Tests
await testEnvironment.StartServerAsync();
var willMessage = new MqttApplicationMessageBuilder().WithTopic("My/last/will").WithAtMostOnceQoS().Build();
var willMessage = new MqttApplicationMessageBuilder().WithTopic("My/last/will").Build();
var clientOptions = new MqttClientOptionsBuilder().WithWillMessage(willMessage);
@@ -171,6 +171,38 @@ namespace MQTTnet.Tests
Assert.AreEqual(0, receivedMessagesCount);
}
}
[TestMethod]
public async Task Will_Message_Do_Not_Send_On_Takeover()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
var receivedMessagesCount = 0;
await testEnvironment.StartServerAsync();
// C1 will receive the last will!
var c1 = await testEnvironment.ConnectClientAsync();
c1.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(c => Interlocked.Increment(ref receivedMessagesCount));
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
// C2 has the last will defined.
var willMessage = new MqttApplicationMessageBuilder().WithTopic("My/last/will").Build();
var clientOptions = new MqttClientOptionsBuilder()
.WithWillMessage(willMessage)
.WithClientId("WillOwner");
var c2 = await testEnvironment.ConnectClientAsync(clientOptions);
// C3 will do the connection takeover.
var c3 = await testEnvironment.ConnectClientAsync(clientOptions);
await Task.Delay(1000);
Assert.AreEqual(0, receivedMessagesCount);
}
}
[TestMethod]
public async Task Will_Message_Send()
@@ -571,7 +603,12 @@ namespace MQTTnet.Tests
using (var testEnvironment = new TestEnvironment(TestContext))
{
var server = await testEnvironment.StartServerAsync();
server.ClientConnectedHandler = new MqttServerClientConnectedHandlerDelegate(e => server.SubscribeAsync(e.ClientId, "topic1"));
server.ClientConnectedHandler = new MqttServerClientConnectedHandlerDelegate(async e =>
{
// Every client will automatically subscribe to this topic.
await server.SubscribeAsync(e.ClientId, "topic1");
});
var client = await testEnvironment.ConnectClientAsync();
var receivedMessages = new List<MqttApplicationMessage>();
@@ -636,11 +673,11 @@ namespace MQTTnet.Tests
Assert.AreEqual(1, clientConnectedCalled);
Assert.AreEqual(0, clientDisconnectedCalled);
await Task.Delay(500);
await Task.Delay(1000);
await c1.DisconnectAsync();
await Task.Delay(500);
await Task.Delay(1000);
Assert.AreEqual(1, clientConnectedCalled);
Assert.AreEqual(1, clientDisconnectedCalled);
@@ -1,5 +1,6 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Server;
using MQTTnet.Server.Internal;
namespace MQTTnet.Tests
{
+5
View File
@@ -31,6 +31,7 @@ namespace MQTTnet.TestApp.NetCore
Console.WriteLine("a = Start QoS 2 benchmark");
Console.WriteLine("b = Start QoS 1 benchmark");
Console.WriteLine("c = Start QoS 0 benchmark");
Console.WriteLine("d = Start server with logging");
var pressedKey = Console.ReadKey(true);
if (pressedKey.KeyChar == '1')
@@ -83,6 +84,10 @@ namespace MQTTnet.TestApp.NetCore
{
Task.Run(PerformanceTest.RunQoS0Test);
}
else if (pressedKey.KeyChar == 'd')
{
Task.Run(ServerTest.RunEmptyServerWithLogging);
}
Thread.Sleep(Timeout.Infinite);
}
@@ -2,8 +2,10 @@
using System.Text;
using System.Threading.Tasks;
using MQTTnet.Client.Receiving;
using MQTTnet.Diagnostics;
using MQTTnet.Protocol;
using MQTTnet.Server;
using MQTTnet.Server.Internal;
namespace MQTTnet.TestApp.NetCore
{
@@ -13,6 +15,19 @@ namespace MQTTnet.TestApp.NetCore
{
var mqttServer = new MqttFactory().CreateMqttServer();
mqttServer.StartAsync(new MqttServerOptions()).GetAwaiter().GetResult();
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
public static void RunEmptyServerWithLogging()
{
var logger = new MqttNetLogger();
MqttNetConsoleLogger.ForwardToConsole(logger);
var mqttFactory = new MqttFactory(logger);
var mqttServer = mqttFactory.CreateMqttServer();
mqttServer.StartAsync(new MqttServerOptions()).GetAwaiter().GetResult();
Console.WriteLine("Press any key to exit.");
Console.ReadLine();