mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-16 17:23:04 +08:00
Compare commits
84
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cea751ff9d | ||
|
|
e88b280eb0 | ||
|
|
e4b07f2b8c | ||
|
|
4eb6e77eb5 | ||
|
|
db1fc7625d | ||
|
|
bfec9dc5ad | ||
|
|
56f0a2c31c | ||
|
|
5ee0bb4b65 | ||
|
|
5c74728387 | ||
|
|
97c46bea0f | ||
|
|
e6c1ab4e4c | ||
|
|
6e1ec266a7 | ||
|
|
3fc6982e9a | ||
|
|
5b1cc7d713 | ||
|
|
2842f27522 | ||
|
|
7ef137e26e | ||
|
|
b1a336eb62 | ||
|
|
d481e93832 | ||
|
|
8cf5537043 | ||
|
|
d2ce9d607a | ||
|
|
36736914bc | ||
|
|
7823d8342e | ||
|
|
431142471f | ||
|
|
a3227a444a | ||
|
|
f74af45fba | ||
|
|
e050e2d61e | ||
|
|
ef85ef676a | ||
|
|
e977546297 | ||
|
|
ae92b0ed33 | ||
|
|
7e686d9aa5 | ||
|
|
d23bbf7bf3 | ||
|
|
d4dad4860a | ||
|
|
c009cbef76 | ||
|
|
37e60c73ae | ||
|
|
3ab095a568 | ||
|
|
f5d9b65139 | ||
|
|
31e5b40459 | ||
|
|
8fb55d75fe | ||
|
|
420222511b | ||
|
|
c5cec60809 | ||
|
|
12dd815421 | ||
|
|
f36f49cb3e | ||
|
|
9152fe637e | ||
|
|
b976e9c177 | ||
|
|
262a4681ad | ||
|
|
ad546adafe | ||
|
|
29a9941d86 | ||
|
|
5a21939e29 | ||
|
|
469caf656e | ||
|
|
42402fba04 | ||
|
|
a0dc8ab0d7 | ||
|
|
02ede5a2d8 | ||
|
|
cef61900df | ||
|
|
09385b1b36 | ||
|
|
cbf413f04e | ||
|
|
11249d006f | ||
|
|
0d7b020994 | ||
|
|
d71ff3d615 | ||
|
|
57745e8324 | ||
|
|
a94776eeea | ||
|
|
d3848d8cb0 | ||
|
|
6c29ea56fc | ||
|
|
ed7c150cc5 | ||
|
|
13762eedd4 | ||
|
|
e33495010e | ||
|
|
b0d1fcd161 | ||
|
|
f7a739e162 | ||
|
|
422945929e | ||
|
|
76e54dc49b | ||
|
|
b10a69a0ae | ||
|
|
4b1c7cf772 | ||
|
|
3f67df007b | ||
|
|
36b1ed06f0 | ||
|
|
4a982ea228 | ||
|
|
9fe5073186 | ||
|
|
305c552474 | ||
|
|
f632fbb447 | ||
|
|
8231d62f9f | ||
|
|
a52a717965 | ||
|
|
2b653f2379 | ||
|
|
725cfb643f | ||
|
|
29ec6edbf4 | ||
|
|
f3ef2dbf7c | ||
|
|
14eb202873 |
@@ -14,6 +14,5 @@ A clear and concise description of what you want to know.
|
|||||||
<!-- Remove the items which don't apply from the following list -->
|
<!-- Remove the items which don't apply from the following list -->
|
||||||
- Client
|
- Client
|
||||||
- ManagedClient
|
- ManagedClient
|
||||||
- MQTTnet.Server standalone
|
|
||||||
- Server
|
- Server
|
||||||
- Generic
|
- Generic
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ A clear and concise description of what the problem is. Example. I'm am trying t
|
|||||||
<!-- Remove the items which don't apply from the following list -->
|
<!-- Remove the items which don't apply from the following list -->
|
||||||
- Client
|
- Client
|
||||||
- ManagedClient
|
- ManagedClient
|
||||||
- MQTTnet.Server standalone
|
|
||||||
- Server
|
- Server
|
||||||
- Generic
|
- Generic
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1 @@
|
|||||||
* [Client] Added the flag _try_private_ (MQTT 3.1.1 Bridge) to client options (#1413).
|
|
||||||
* [Client] Fixed MQTTv5 protocol violation in PUBLISH packets (#1423).
|
|
||||||
* [Client] Added missing "WithWill..." methods in _MqttClientOptionsBuilder_.
|
|
||||||
* [ManagedClient] Fixed wrong event args type for connected and disconnected events (#1432).
|
|
||||||
* [Server] Fixed wrong handling of retained messages with QoS > 0 (#1434).
|
|
||||||
* [Server] Connections with _try_private_ flag (MQTT 3.1.1 Bridge) are now accepted (#1413).
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name: CI
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
VERSION: "4.0.1.${{github.run_number}}"
|
VERSION: "4.1.3.${{github.run_number}}"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/ProcessingInstructionAttributesFormat/@EntryValue">OnDifferentLines</s:String>
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/ProcessingInstructionAttributesFormat/@EntryValue">OnDifferentLines</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/TagAttributesFormat/@EntryValue">OnDifferentLines</s:String>
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/TagAttributesFormat/@EntryValue">OnDifferentLines</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue"><?xml version="1.0" encoding="utf-16"?>
|
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue"><?xml version="1.0" encoding="utf-16"?>
|
||||||
<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
|
<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
|
||||||
<TypePattern DisplayName="Non-reorderable types">
|
<TypePattern DisplayName="Non-reorderable types">
|
||||||
<TypePattern.Match>
|
<TypePattern.Match>
|
||||||
<Or>
|
<Or>
|
||||||
@@ -210,6 +210,7 @@
|
|||||||
</Or>
|
</Or>
|
||||||
</Entry.Match>
|
</Entry.Match>
|
||||||
<Entry.SortBy>
|
<Entry.SortBy>
|
||||||
|
<Access Order="Public Internal Protected Private" />
|
||||||
<Name />
|
<Name />
|
||||||
</Entry.SortBy>
|
</Entry.SortBy>
|
||||||
</Entry>
|
</Entry>
|
||||||
@@ -226,6 +227,9 @@
|
|||||||
</Entry>
|
</Entry>
|
||||||
</TypePattern>
|
</TypePattern>
|
||||||
</Patterns></s:String>
|
</Patterns></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">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.</s:String>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
@@ -237,5 +241,7 @@
|
|||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGRESP/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBCOMP/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Tnet/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Tnet/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
# MQTTnet
|
# MQTTnet
|
||||||
|
|
||||||
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports the MQTT protocol up to version 5.
|
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports the MQTT protocol up to version 5. It is compatible with mostly any supported .NET Framework version and CPU architecture.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -50,39 +50,6 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
|||||||
* A custom message interceptor can be added which allows transforming or extending every received application message
|
* A custom message interceptor can be added which allows transforming or extending every received application message
|
||||||
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
||||||
|
|
||||||
## Supported frameworks
|
|
||||||
|
|
||||||
| Framwork | Version |
|
|
||||||
| ------------------ | ----------- |
|
|
||||||
|.NET | 5.0+ |
|
|
||||||
|.NET Framework | 4.5.2+ |
|
|
||||||
|.NET Standard | 1.3+ |
|
|
||||||
|.NET Core | 1.1+ |
|
|
||||||
|.NET Core App | 1.1+ |
|
|
||||||
| Mono | 5.2+ |
|
|
||||||
| UWP | 10.0.10240+ |
|
|
||||||
| Xamarin.Android | 7.5+ |
|
|
||||||
| Xamarin.iOS | 10.14+ |
|
|
||||||
| Blazor WebAssembly | 3.2.0+ |
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
This library is used in the following projects:
|
|
||||||
|
|
||||||
* Azure Functions MQTT Bindings, <https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt>)
|
|
||||||
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
|
|
||||||
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
|
|
||||||
* MQTT Client Rx (Managed Client Wrapper for Reactive Extensions, <https://github.com/mmuecke/RxMQTTnet>)
|
|
||||||
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
|
|
||||||
* MQTTnet App (Cross platform client application for MQTT debugging, inspection etc., <https://github.com/chkr1011/MQTTnetApp>)
|
|
||||||
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
|
|
||||||
* SparkplugNet (Sparkplug library for .Net, <https://github.com/SeppPenner/SparkplugNet>)
|
|
||||||
* Silverback (Framework to build event-driven applications - support for MQTT, Kafka & RabbitMQ) <https://github.com/BEagle1984/silverback>
|
|
||||||
|
|
||||||
Further projects using this project can be found under https://github.com/dotnet/MQTTnet/network/dependents.
|
|
||||||
|
|
||||||
If you use this library and want to see your project here please create a pull request.
|
|
||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
|
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
|
||||||
|
|||||||
@@ -2,8 +2,13 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Extensions.WebSocket4Net;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Samples.Helpers;
|
using MQTTnet.Samples.Helpers;
|
||||||
|
|
||||||
@@ -111,13 +116,45 @@ public static class Client_Connection_Samples
|
|||||||
.WithTls(
|
.WithTls(
|
||||||
o =>
|
o =>
|
||||||
{
|
{
|
||||||
|
// The used public broker sometimes has invalid certificates. This sample accepts all
|
||||||
|
// certificates. This should not be used in live environments.
|
||||||
|
o.CertificateValidationHandler = _ => true;
|
||||||
|
|
||||||
|
// The default value is determined by the OS. Set manually to force version.
|
||||||
o.SslProtocol = SslProtocols.Tls12;
|
o.SslProtocol = SslProtocols.Tls12;
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
using (var timeout = new CancellationTokenSource(5000))
|
||||||
|
{
|
||||||
|
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
|
||||||
|
|
||||||
|
Console.WriteLine("The MQTT client is connected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Connect_Client_Using_WebSocket4Net()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
|
||||||
|
* Instead of the .NET implementation of WebSockets the implementaion from WebSocket4Net is used. It provides more
|
||||||
|
* encryption algorithms and supports more platforms.
|
||||||
|
*
|
||||||
|
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory().UseWebSocket4Net();
|
||||||
|
|
||||||
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
|
{
|
||||||
|
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").Build();
|
||||||
|
|
||||||
|
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
Console.WriteLine("The MQTT client is connected.");
|
Console.WriteLine("The MQTT client is connected.");
|
||||||
|
|
||||||
|
response.DumpToConsole();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,16 +196,99 @@ public static class Client_Connection_Samples
|
|||||||
.WithTls(
|
.WithTls(
|
||||||
o =>
|
o =>
|
||||||
{
|
{
|
||||||
o.SslProtocol = SslProtocols.Tls12; // The default value is determined by the OS. Set manually to force version.
|
// The used public broker sometimes has invalid certificates. This sample accepts all
|
||||||
|
// certificates. This should not be used in live environments.
|
||||||
|
o.CertificateValidationHandler = _ => true;
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// In MQTTv5 the response contains much more information.
|
// In MQTTv5 the response contains much more information.
|
||||||
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
using (var timeout = new CancellationTokenSource(5000))
|
||||||
|
{
|
||||||
|
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
|
||||||
|
|
||||||
Console.WriteLine("The MQTT client is connected.");
|
Console.WriteLine("The MQTT client is connected.");
|
||||||
|
|
||||||
response.DumpToConsole();
|
response.DumpToConsole();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Disconnect_Clean()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample disconnects from the server with sending a DISCONNECT packet.
|
||||||
|
* This way of disconnecting is treated as a clean disconnect which will not
|
||||||
|
* trigger sending the last will etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
|
{
|
||||||
|
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
|
|
||||||
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
|
// Calling _DisconnectAsync_ will send a DISCONNECT packet before closing the connection.
|
||||||
|
// Using a reason code requires MQTT version 5.0.0!
|
||||||
|
await mqttClient.DisconnectAsync(MqttClientDisconnectReason.ImplementationSpecificError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Disconnect_Non_Clean()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample disconnects from the server without sending a DISCONNECT packet.
|
||||||
|
* This way of disconnecting is treated as a non clean disconnect which will
|
||||||
|
* trigger sending the last will etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
|
var mqttClient = mqttFactory.CreateMqttClient();
|
||||||
|
|
||||||
|
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
|
|
||||||
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
|
// Calling _Dispose_ or use of a _using_ statement will close the transport connection
|
||||||
|
// without sending a DISCONNECT packet to the server.
|
||||||
|
mqttClient.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Inspect_Certificate_Validation_Errors()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample prints the certificate information while connection. This data can be used to decide whether a connection is secure or not
|
||||||
|
* including the reason for that status.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
|
{
|
||||||
|
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io", 8883)
|
||||||
|
.WithTls(
|
||||||
|
o =>
|
||||||
|
{
|
||||||
|
o.CertificateValidationHandler = eventArgs =>
|
||||||
|
{
|
||||||
|
eventArgs.Certificate.Subject.DumpToConsole();
|
||||||
|
eventArgs.Certificate.GetExpirationDateString().DumpToConsole();
|
||||||
|
eventArgs.Chain.ChainPolicy.RevocationMode.DumpToConsole();
|
||||||
|
eventArgs.Chain.ChainStatus.DumpToConsole();
|
||||||
|
eventArgs.SslPolicyErrors.DumpToConsole();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// In MQTTv5 the response contains much more information.
|
||||||
|
using (var timeout = new CancellationTokenSource(5000))
|
||||||
|
{
|
||||||
|
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,14 +364,12 @@ public static class Client_Connection_Samples
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// This code will also do the very first connect! So no call to _ConnectAsync_ is required
|
// This code will also do the very first connect! So no call to _ConnectAsync_ is required in the first place.
|
||||||
// in the first place.
|
if (!await mqttClient.TryPingAsync())
|
||||||
if (!mqttClient.IsConnected)
|
|
||||||
{
|
{
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
// Subscribe to topics when session is clean etc.
|
// Subscribe to topics when session is clean etc.
|
||||||
|
|
||||||
Console.WriteLine("The MQTT client is connected.");
|
Console.WriteLine("The MQTT client is connected.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Client;
|
namespace MQTTnet.Samples.Client;
|
||||||
@@ -36,6 +40,53 @@ public static class Client_Publish_Samples
|
|||||||
|
|
||||||
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
||||||
|
|
||||||
|
await mqttClient.DisconnectAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("MQTT application message is published.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Publish_Multiple_Application_Messages()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample pushes multiple simple application message including a topic and a payload.
|
||||||
|
*
|
||||||
|
* See sample _Publish_Application_Message_ for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
|
{
|
||||||
|
var mqttClientOptions = new MqttClientOptionsBuilder()
|
||||||
|
.WithTcpServer("broker.hivemq.com")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
|
var applicationMessage = new MqttApplicationMessageBuilder()
|
||||||
|
.WithTopic("samples/temperature/living_room")
|
||||||
|
.WithPayload("19.5")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
||||||
|
|
||||||
|
applicationMessage = new MqttApplicationMessageBuilder()
|
||||||
|
.WithTopic("samples/temperature/living_room")
|
||||||
|
.WithPayload("20.0")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
||||||
|
|
||||||
|
applicationMessage = new MqttApplicationMessageBuilder()
|
||||||
|
.WithTopic("samples/temperature/living_room")
|
||||||
|
.WithPayload("21.0")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
||||||
|
|
||||||
|
await mqttClient.DisconnectAsync();
|
||||||
|
|
||||||
Console.WriteLine("MQTT application message is published.");
|
Console.WriteLine("MQTT application message is published.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,13 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Packets;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Samples.Helpers;
|
using MQTTnet.Samples.Helpers;
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Client;
|
namespace MQTTnet.Samples.Client;
|
||||||
@@ -19,9 +25,7 @@ public static class Client_Subscribe_Samples
|
|||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
{
|
{
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
.WithTcpServer("broker.hivemq.com")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
// Setup message handling before connecting so that queued messages
|
// Setup message handling before connecting so that queued messages
|
||||||
// are also handled properly. When there is no event handler attached all
|
// are also handled properly. When there is no event handler attached all
|
||||||
@@ -37,7 +41,11 @@ public static class Client_Subscribe_Samples
|
|||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||||
.WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/2"); })
|
.WithTopicFilter(
|
||||||
|
f =>
|
||||||
|
{
|
||||||
|
f.WithTopic("mqttnet/samples/topic/2");
|
||||||
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||||
@@ -49,6 +57,95 @@ public static class Client_Subscribe_Samples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task Send_Responses()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample subscribes to a topic and sends a response to the broker. This requires at least QoS level 1 to work!
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
|
{
|
||||||
|
mqttClient.ApplicationMessageReceivedAsync += delegate(MqttApplicationMessageReceivedEventArgs args)
|
||||||
|
{
|
||||||
|
// Do some work with the message...
|
||||||
|
|
||||||
|
// Now respond to the broker with a reason code other than success.
|
||||||
|
args.ReasonCode = MqttApplicationMessageReceivedReasonCode.ImplementationSpecificError;
|
||||||
|
args.ResponseReasonString = "That did not work!";
|
||||||
|
|
||||||
|
// User properties require MQTT v5!
|
||||||
|
args.ResponseUserProperties.Add(new MqttUserProperty("My", "Data"));
|
||||||
|
|
||||||
|
// Now the broker will resend the message again.
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
|
|
||||||
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
|
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||||
|
.WithTopicFilter(
|
||||||
|
f =>
|
||||||
|
{
|
||||||
|
f.WithTopic("mqttnet/samples/topic/1");
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||||
|
|
||||||
|
Console.WriteLine("MQTT client subscribed to topic.");
|
||||||
|
|
||||||
|
// The response contains additional data sent by the server after subscribing.
|
||||||
|
response.DumpToConsole();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Subscribe_Multiple_Topics()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample subscribes to several topics in a single request.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
|
{
|
||||||
|
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
|
|
||||||
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
|
// Create the subscribe options including several topics with different options.
|
||||||
|
// It is also possible to all of these topics using a dedicated call of _SubscribeAsync_ per topic.
|
||||||
|
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||||
|
.WithTopicFilter(
|
||||||
|
f =>
|
||||||
|
{
|
||||||
|
f.WithTopic("mqttnet/samples/topic/1");
|
||||||
|
})
|
||||||
|
.WithTopicFilter(
|
||||||
|
f =>
|
||||||
|
{
|
||||||
|
f.WithTopic("mqttnet/samples/topic/2").WithNoLocal();
|
||||||
|
})
|
||||||
|
.WithTopicFilter(
|
||||||
|
f =>
|
||||||
|
{
|
||||||
|
f.WithTopic("mqttnet/samples/topic/3").WithRetainHandling(MqttRetainHandling.SendAtSubscribe);
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||||
|
|
||||||
|
Console.WriteLine("MQTT client subscribed to topics.");
|
||||||
|
|
||||||
|
// The response contains additional data sent by the server after subscribing.
|
||||||
|
response.DumpToConsole();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task Subscribe_Topic()
|
public static async Task Subscribe_Topic()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -59,14 +156,16 @@ public static class Client_Subscribe_Samples
|
|||||||
|
|
||||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||||
{
|
{
|
||||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||||
.WithTcpServer("broker.hivemq.com")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|
||||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||||
.WithTopicFilter(f => { f.WithTopic("mqttnet/samples/topic/1"); })
|
.WithTopicFilter(
|
||||||
|
f =>
|
||||||
|
{
|
||||||
|
f.WithTopic("mqttnet/samples/topic/1");
|
||||||
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
|
|
||||||
namespace MQTTnet.Samples.Diagnostics;
|
namespace MQTTnet.Samples.Diagnostics;
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||||
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
|
||||||
|
|
||||||
<ProjectReference Include="..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
|
<ProjectReference Include="..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj" />
|
||||||
<ProjectReference Include="..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj" />
|
<ProjectReference Include="..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj" />
|
||||||
<ProjectReference Include="..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj" />
|
<ProjectReference Include="..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj" />
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Extensions.ManagedClient;
|
using MQTTnet.Extensions.ManagedClient;
|
||||||
|
|
||||||
|
|||||||
+9
-3
@@ -39,10 +39,16 @@ Console.WriteLine();
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var task = selectedSampleMethod.Invoke(null, null) as Task;
|
if (selectedSampleMethod.Invoke(null, null) is Task task)
|
||||||
task?.Wait();
|
{
|
||||||
|
await task;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
Console.WriteLine(exception.ToString());
|
Console.WriteLine(exception.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("Press Enter to exit.");
|
||||||
|
Console.ReadLine();
|
||||||
@@ -2,6 +2,10 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Extensions.Rpc;
|
using MQTTnet.Extensions.Rpc;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
// ReSharper disable EmptyConstructor
|
||||||
|
// ReSharper disable MemberCanBeMadeStatic.Local
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using MQTTnet.AspNetCore;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
|
namespace MQTTnet.Samples.Server;
|
||||||
|
|
||||||
|
public static class Server_Asp_Net_Samples
|
||||||
|
{
|
||||||
|
public static Task Start_Server_With_WebSockets_Support()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample starts a minimal ASP.NET Webserver including a hosted MQTT server.
|
||||||
|
*/
|
||||||
|
var host = Host.CreateDefaultBuilder(Array.Empty<string>())
|
||||||
|
.ConfigureWebHostDefaults(
|
||||||
|
webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.UseKestrel(
|
||||||
|
o =>
|
||||||
|
{
|
||||||
|
// This will allow MQTT connections based on TCP port 1883.
|
||||||
|
o.ListenAnyIP(1883, l => l.UseMqtt());
|
||||||
|
|
||||||
|
// This will allow MQTT connections based on HTTP WebSockets with URI "localhost:5000/mqtt"
|
||||||
|
// See code below for URI configuration.
|
||||||
|
o.ListenAnyIP(5000); // Default HTTP pipeline
|
||||||
|
});
|
||||||
|
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
});
|
||||||
|
|
||||||
|
return host.RunConsoleAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class MqttController
|
||||||
|
{
|
||||||
|
public MqttController()
|
||||||
|
{
|
||||||
|
// Inject other services via constructor.
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task OnClientConnected(ClientConnectedEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Client '{eventArgs.ClientId}' connected.");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task ValidateConnection(ValidatingConnectionEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Client '{eventArgs.ClientId}' wants to connect. Accepting!");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Startup
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment environment, MqttController mqttController)
|
||||||
|
{
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseEndpoints(
|
||||||
|
endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapConnectionHandler<MqttConnectionHandler>(
|
||||||
|
"/mqtt",
|
||||||
|
httpConnectionDispatcherOptions => httpConnectionDispatcherOptions.WebSockets.SubProtocolSelector =
|
||||||
|
protocolList => protocolList.FirstOrDefault() ?? string.Empty);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseMqttServer(
|
||||||
|
server =>
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Attach event handlers etc. if required.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.ValidatingConnectionAsync += mqttController.ValidateConnection;
|
||||||
|
server.ClientConnectedAsync += mqttController.OnClientConnected;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddHostedMqttServer(
|
||||||
|
optionsBuilder =>
|
||||||
|
{
|
||||||
|
optionsBuilder.WithDefaultEndpoint();
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddMqttConnectionHandler();
|
||||||
|
services.AddConnections();
|
||||||
|
|
||||||
|
services.AddSingleton<MqttController>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
// ReSharper disable EmptyConstructor
|
||||||
|
// ReSharper disable MemberCanBeMadeStatic.Local
|
||||||
|
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
|
namespace MQTTnet.Samples.Server;
|
||||||
|
|
||||||
|
public static class Server_Intercepting_Samples
|
||||||
|
{
|
||||||
|
public static async Task Intercept_Application_Messages()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample starts a simple MQTT server which manipulate all processed application messages.
|
||||||
|
* Please see _Server_Simple_Samples_ for more details on how to start a server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||||
|
|
||||||
|
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
||||||
|
{
|
||||||
|
mqttServer.InterceptingPublishAsync += args =>
|
||||||
|
{
|
||||||
|
// Here we only change the topic of the received application message.
|
||||||
|
// but also changing the payload etc. is required. Changing the QoS after
|
||||||
|
// transmitting is not supported and makes no sense at all.
|
||||||
|
args.ApplicationMessage.Topic += "/manipulated";
|
||||||
|
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
await mqttServer.StartAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("Press Enter to exit.");
|
||||||
|
Console.ReadLine();
|
||||||
|
await mqttServer.StopAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace MQTTnet.Samples.Server;
|
||||||
|
|
||||||
|
public static class Server_Retained_Messages_Samples
|
||||||
|
{
|
||||||
|
public static async Task Persist_Retained_Messages()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample starts a simple MQTT server which will store all retained messages in a file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var storePath = Path.Combine(Path.GetTempPath(), "RetainedMessages.json");
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
|
// Due to security reasons the "default" endpoint (which is unencrypted) is not enabled by default!
|
||||||
|
var mqttServerOptions = mqttFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||||
|
|
||||||
|
using (var server = mqttFactory.CreateMqttServer(mqttServerOptions))
|
||||||
|
{
|
||||||
|
// Make sure that the server will load the retained messages.
|
||||||
|
server.LoadingRetainedMessageAsync += async eventArgs =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
eventArgs.LoadedRetainedMessages = await JsonSerializer.DeserializeAsync<List<MqttApplicationMessage>>(File.OpenRead(storePath));
|
||||||
|
Console.WriteLine("Retained messages loaded.");
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
// Ignore because nothing is stored yet.
|
||||||
|
Console.WriteLine("No retained messages stored yet.");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Console.WriteLine(exception);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure to persist the changed retained messages.
|
||||||
|
server.RetainedMessageChangedAsync += async eventArgs =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// This sample uses the property _StoredRetainedMessages_ which will contain all(!) retained messages.
|
||||||
|
// The event args also contain the affected retained message (property ChangedRetainedMessage). This can be
|
||||||
|
// used to write all retained messages to dedicated files etc. Then all files must be loaded and a full list
|
||||||
|
// of retained messages must be provided in the loaded event.
|
||||||
|
|
||||||
|
var buffer = JsonSerializer.SerializeToUtf8Bytes(eventArgs.StoredRetainedMessages);
|
||||||
|
await File.WriteAllBytesAsync(storePath, buffer);
|
||||||
|
Console.WriteLine("Retained messages saved.");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Console.WriteLine(exception);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure to clear the retained messages when they are all deleted via API.
|
||||||
|
server.RetainedMessagesClearedAsync += _ =>
|
||||||
|
{
|
||||||
|
File.Delete(storePath);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
await server.StartAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("Press Enter to exit.");
|
||||||
|
Console.ReadLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,11 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
@@ -9,27 +14,95 @@ namespace MQTTnet.Samples.Server;
|
|||||||
|
|
||||||
public static class Server_Simple_Samples
|
public static class Server_Simple_Samples
|
||||||
{
|
{
|
||||||
|
public static async Task Force_Disconnecting_Client()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample will disconnect a client.
|
||||||
|
*
|
||||||
|
* See _Run_Minimal_Server_ for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using (var mqttServer = await StartMqttServer())
|
||||||
|
{
|
||||||
|
// Let the client connect.
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
|
// Now disconnect the client (if connected).
|
||||||
|
var affectedClient = (await mqttServer.GetClientsAsync()).FirstOrDefault(c => c.Id == "MyClient");
|
||||||
|
if (affectedClient != null)
|
||||||
|
{
|
||||||
|
await affectedClient.DisconnectAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Publish_Message_From_Broker()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample will publish a message directly at the broker.
|
||||||
|
*
|
||||||
|
* See _Run_Minimal_Server_ for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using (var mqttServer = await StartMqttServer())
|
||||||
|
{
|
||||||
|
// Create a new message using the builder as usual.
|
||||||
|
var message = new MqttApplicationMessageBuilder().WithTopic("HelloWorld").WithPayload("Test").Build();
|
||||||
|
|
||||||
|
// Now inject the new message at the broker.
|
||||||
|
await mqttServer.InjectApplicationMessage(
|
||||||
|
new InjectedMqttApplicationMessage(message)
|
||||||
|
{
|
||||||
|
SenderClientId = "SenderClientId"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task Run_Minimal_Server()
|
public static async Task Run_Minimal_Server()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This sample starts a simple MQTT server which will accept any TCP connection.
|
* This sample starts a simple MQTT server which will accept any TCP connection.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
// The port for the default endpoint is 1883.
|
// The port for the default endpoint is 1883.
|
||||||
// The default endpoint is NOT encrypted!
|
// The default endpoint is NOT encrypted!
|
||||||
// Use the builder classes where possible.
|
// Use the builder classes where possible.
|
||||||
var mqttServerOptions = new MqttServerOptionsBuilder()
|
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||||
.WithDefaultEndpoint()
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
// The port can be changed using the following API (not used in this example).
|
// The port can be changed using the following API (not used in this example).
|
||||||
new MqttServerOptionsBuilder()
|
// new MqttServerOptionsBuilder()
|
||||||
.WithDefaultEndpoint()
|
// .WithDefaultEndpoint()
|
||||||
.WithDefaultEndpointPort(1234)
|
// .WithDefaultEndpointPort(1234)
|
||||||
.Build();
|
// .Build();
|
||||||
|
|
||||||
|
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
||||||
|
{
|
||||||
|
await mqttServer.StartAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("Press Enter to exit.");
|
||||||
|
Console.ReadLine();
|
||||||
|
|
||||||
|
// Stop and dispose the MQTT server if it is no longer needed!
|
||||||
|
await mqttServer.StopAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Run_Server_With_Logging()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample starts a simple MQTT server and prints the logs to the output.
|
||||||
|
*
|
||||||
|
* IMPORTANT! Do not enable logging in live environment. It will decrease performance.
|
||||||
|
*
|
||||||
|
* See sample "Run_Minimal_Server" for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory(new ConsoleLogger());
|
||||||
|
|
||||||
|
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||||
|
|
||||||
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
||||||
{
|
{
|
||||||
await mqttServer.StartAsync();
|
await mqttServer.StartAsync();
|
||||||
@@ -52,9 +125,7 @@ public static class Server_Simple_Samples
|
|||||||
|
|
||||||
var mqttFactory = new MqttFactory();
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
var mqttServerOptions = new MqttServerOptionsBuilder()
|
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||||
.WithDefaultEndpoint()
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
||||||
{
|
{
|
||||||
@@ -67,7 +138,7 @@ public static class Server_Simple_Samples
|
|||||||
e.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
|
e.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.Username != "ValidUser")
|
if (e.UserName != "ValidUser")
|
||||||
{
|
{
|
||||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||||
}
|
}
|
||||||
@@ -88,4 +159,61 @@ public static class Server_Simple_Samples
|
|||||||
await mqttServer.StopAsync();
|
await mqttServer.StopAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static async Task<MqttServer> StartMqttServer()
|
||||||
|
{
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
|
// Due to security reasons the "default" endpoint (which is unencrypted) is not enabled by default!
|
||||||
|
var mqttServerOptions = mqttFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||||
|
var server = mqttFactory.CreateMqttServer(mqttServerOptions);
|
||||||
|
await server.StartAsync();
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConsoleLogger : IMqttNetLogger
|
||||||
|
{
|
||||||
|
readonly object _consoleSyncRoot = new();
|
||||||
|
|
||||||
|
public bool IsEnabled => true;
|
||||||
|
|
||||||
|
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[]? parameters, Exception? exception)
|
||||||
|
{
|
||||||
|
var foregroundColor = ConsoleColor.White;
|
||||||
|
switch (logLevel)
|
||||||
|
{
|
||||||
|
case MqttNetLogLevel.Verbose:
|
||||||
|
foregroundColor = ConsoleColor.White;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MqttNetLogLevel.Info:
|
||||||
|
foregroundColor = ConsoleColor.Green;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MqttNetLogLevel.Warning:
|
||||||
|
foregroundColor = ConsoleColor.DarkYellow;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MqttNetLogLevel.Error:
|
||||||
|
foregroundColor = ConsoleColor.Red;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters?.Length > 0)
|
||||||
|
{
|
||||||
|
message = string.Format(message, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_consoleSyncRoot)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = foregroundColor;
|
||||||
|
Console.WriteLine(message);
|
||||||
|
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||||
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO.Pipelines;
|
|
||||||
using Microsoft.AspNetCore.Connections;
|
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
|
||||||
{
|
|
||||||
public class ConnectionContextMockup : ConnectionContext
|
|
||||||
{
|
|
||||||
public override string ConnectionId { get; set; }
|
|
||||||
|
|
||||||
public override IFeatureCollection Features { get; }
|
|
||||||
|
|
||||||
public override IDictionary<object, object> Items { get; set; }
|
|
||||||
public override IDuplexPipe Transport { get; set; }
|
|
||||||
|
|
||||||
public ConnectionContextMockup()
|
|
||||||
{
|
|
||||||
//Transport = new DefaultConnectionContext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,20 +2,24 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Connections;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Formatter;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Connections;
|
||||||
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
|
using MQTTnet.Formatter;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||||
{
|
{
|
||||||
public class ConnectionHandlerMockup: IMqttServerAdapter
|
public sealed class ConnectionHandlerMockup : IMqttServerAdapter
|
||||||
{
|
{
|
||||||
public TaskCompletionSource<MqttConnectionContext> Context { get; } = new TaskCompletionSource<MqttConnectionContext>();
|
|
||||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||||
|
public TaskCompletionSource<MqttConnectionContext> Context { get; } = new TaskCompletionSource<MqttConnectionContext>();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public async Task OnConnectedAsync(ConnectionContext connection)
|
public async Task OnConnectedAsync(ConnectionContext connection)
|
||||||
{
|
{
|
||||||
@@ -42,9 +46,5 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
|
|||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ using System.IO.Pipelines;
|
|||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||||
{
|
{
|
||||||
public class DuplexPipeMockup : IDuplexPipe
|
public sealed class DuplexPipeMockup : IDuplexPipe
|
||||||
{
|
{
|
||||||
public DuplexPipeMockup()
|
public DuplexPipeMockup()
|
||||||
{
|
{
|
||||||
@@ -20,9 +20,8 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
|
|||||||
|
|
||||||
PipeWriter IDuplexPipe.Output => Send.Writer;
|
PipeWriter IDuplexPipe.Output => Send.Writer;
|
||||||
|
|
||||||
|
public Pipe Receive { get; }
|
||||||
|
|
||||||
public Pipe Receive { get; set; }
|
public Pipe Send { get; }
|
||||||
|
|
||||||
public Pipe Send { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using System.Buffers;
|
|||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||||
{
|
{
|
||||||
public class LimitedMemoryPool : MemoryPool<byte>
|
public sealed class LimitedMemoryPool : MemoryPool<byte>
|
||||||
{
|
{
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ using System.Buffers;
|
|||||||
|
|
||||||
namespace MQTTnet.AspNetCore.Tests.Mockups
|
namespace MQTTnet.AspNetCore.Tests.Mockups
|
||||||
{
|
{
|
||||||
public class MemoryOwner : IMemoryOwner<byte>
|
public sealed class MemoryOwner : IMemoryOwner<byte>
|
||||||
{
|
{
|
||||||
private readonly byte[] _raw;
|
readonly byte[] _raw;
|
||||||
|
|
||||||
public MemoryOwner(int size)
|
public MemoryOwner(int size)
|
||||||
{
|
{
|
||||||
@@ -27,11 +27,11 @@ namespace MQTTnet.AspNetCore.Tests.Mockups
|
|||||||
Memory = _raw;
|
Memory = _raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Memory<byte> Memory { get; }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
ArrayPool<byte>.Shared.Return(_raw);
|
ArrayPool<byte>.Shared.Return(_raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Memory<byte> Memory { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,22 +2,21 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Connections;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Connections;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.AspNetCore.Tests.Mockups;
|
using MQTTnet.AspNetCore.Tests.Mockups;
|
||||||
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Tests.Extensions;
|
using MQTTnet.Tests.Extensions;
|
||||||
|
|
||||||
@@ -26,20 +25,6 @@ namespace MQTTnet.AspNetCore.Tests
|
|||||||
[TestClass]
|
[TestClass]
|
||||||
public class MqttConnectionContextTest
|
public class MqttConnectionContextTest
|
||||||
{
|
{
|
||||||
[TestMethod]
|
|
||||||
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
|
|
||||||
{
|
|
||||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
|
||||||
var pipe = new DuplexPipeMockup();
|
|
||||||
var connection = new DefaultConnectionContext();
|
|
||||||
connection.Transport = pipe;
|
|
||||||
var ctx = new MqttConnectionContext(serializer, connection);
|
|
||||||
|
|
||||||
pipe.Receive.Writer.Complete();
|
|
||||||
|
|
||||||
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task TestCorruptedConnectPacket()
|
public async Task TestCorruptedConnectPacket()
|
||||||
{
|
{
|
||||||
@@ -49,13 +34,43 @@ namespace MQTTnet.AspNetCore.Tests
|
|||||||
var connection = new DefaultConnectionContext();
|
var connection = new DefaultConnectionContext();
|
||||||
connection.Transport = pipe;
|
connection.Transport = pipe;
|
||||||
var ctx = new MqttConnectionContext(serializer, connection);
|
var ctx = new MqttConnectionContext(serializer, connection);
|
||||||
|
|
||||||
await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, new byte[0]));
|
await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, new byte[0]));
|
||||||
|
|
||||||
await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||||
|
|
||||||
// the first exception should complete the pipes so if someone tries to use the connection after that it should throw immidiatly
|
// the first exception should complete the pipes so if someone tries to use the connection after that it should throw immidiatly
|
||||||
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task TestEndpoint()
|
||||||
|
{
|
||||||
|
var mockup = new ConnectionHandlerMockup();
|
||||||
|
|
||||||
|
|
||||||
|
using (var host = new WebHostBuilder().UseKestrel(kestrel => kestrel.ListenLocalhost(1883, listener => listener.Use((ctx, next) => mockup.OnConnectedAsync(ctx))))
|
||||||
|
.UseStartup<Startup>()
|
||||||
|
.ConfigureServices(
|
||||||
|
(hostContext, services) =>
|
||||||
|
{
|
||||||
|
services.AddHostedMqttServer(o => o.WithoutDefaultEndpoint());
|
||||||
|
services.AddSingleton<IMqttServerAdapter>(mockup);
|
||||||
|
})
|
||||||
|
.Build())
|
||||||
|
|
||||||
|
using (var client = new MqttFactory().CreateMqttClient())
|
||||||
|
{
|
||||||
|
host.Start();
|
||||||
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build(), CancellationToken.None);
|
||||||
|
|
||||||
|
var ctx = await mockup.Context.Task;
|
||||||
|
#if NETCOREAPP3_1
|
||||||
|
var ep = IPEndPoint.Parse(ctx.Endpoint);
|
||||||
|
Assert.IsNotNull(ep);
|
||||||
|
#endif
|
||||||
|
Assert.IsNotNull(ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// COMMENTED OUT DUE TO DEAD LOCK? OR VERY VERY SLOW PERFORMANCE ON LOCAL DEV MACHINE. TEST WAS STILL RUNNING AFTER SEVERAL MINUTES!
|
// COMMENTED OUT DUE TO DEAD LOCK? OR VERY VERY SLOW PERFORMANCE ON LOCAL DEV MACHINE. TEST WAS STILL RUNNING AFTER SEVERAL MINUTES!
|
||||||
@@ -78,7 +93,7 @@ namespace MQTTnet.AspNetCore.Tests
|
|||||||
|
|
||||||
// await Task.WhenAll(tasks).ConfigureAwait(false);
|
// await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task TestLargePacket()
|
public async Task TestLargePacket()
|
||||||
{
|
{
|
||||||
@@ -94,43 +109,25 @@ namespace MQTTnet.AspNetCore.Tests
|
|||||||
Assert.IsTrue(readResult.Buffer.Length > 20000);
|
Assert.IsTrue(readResult.Buffer.Length > 20000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Startup
|
[TestMethod]
|
||||||
|
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
|
||||||
|
{
|
||||||
|
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||||
|
var pipe = new DuplexPipeMockup();
|
||||||
|
var connection = new DefaultConnectionContext();
|
||||||
|
connection.Transport = pipe;
|
||||||
|
var ctx = new MqttConnectionContext(serializer, connection);
|
||||||
|
|
||||||
|
pipe.Receive.Writer.Complete();
|
||||||
|
|
||||||
|
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
class Startup
|
||||||
{
|
{
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task TestEndpoint()
|
|
||||||
{
|
|
||||||
var mockup = new ConnectionHandlerMockup();
|
|
||||||
|
|
||||||
|
|
||||||
using (var host = new WebHostBuilder()
|
|
||||||
.UseKestrel(kestrel => kestrel.ListenLocalhost(1883, listener => listener.Use((ctx, next) => mockup.OnConnectedAsync(ctx))))
|
|
||||||
.UseStartup<Startup>()
|
|
||||||
.ConfigureServices((hostContext, services) =>
|
|
||||||
{
|
|
||||||
services.AddHostedMqttServer(o => o.WithoutDefaultEndpoint());
|
|
||||||
services.AddSingleton<IMqttServerAdapter>(mockup);
|
|
||||||
})
|
|
||||||
.Build())
|
|
||||||
|
|
||||||
using (var client = new MqttFactory().CreateMqttClient())
|
|
||||||
{
|
|
||||||
host.Start();
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder()
|
|
||||||
.WithTcpServer("localhost")
|
|
||||||
.Build(), CancellationToken.None);
|
|
||||||
|
|
||||||
var ctx = await mockup.Context.Task;
|
|
||||||
#if NETCOREAPP3_1
|
|
||||||
var ep = IPEndPoint.Parse(ctx.Endpoint);
|
|
||||||
Assert.IsNotNull(ep);
|
|
||||||
#endif
|
|
||||||
Assert.IsNotNull(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ using MQTTnet.Packets;
|
|||||||
namespace MQTTnet.AspNetCore.Tests
|
namespace MQTTnet.AspNetCore.Tests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class ReaderExtensionsTest
|
public sealed class ReaderExtensionsTest
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestTryDeserialize()
|
public void TestTryDeserialize()
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ namespace MQTTnet.AspNetCore
|
|||||||
{
|
{
|
||||||
var formatter = PacketFormatterAdapter;
|
var formatter = PacketFormatterAdapter;
|
||||||
|
|
||||||
using (await _writerLock.WaitAsync(cancellationToken).ConfigureAwait(false))
|
using (await _writerLock.EnterAsync(cancellationToken).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
var buffer = formatter.Encode(packet);
|
var buffer = formatter.Encode(packet);
|
||||||
var msg = buffer.Join().AsMemory();
|
var msg = buffer.Join().AsMemory();
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// 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 BenchmarkDotNet.Attributes;
|
||||||
|
using BenchmarkDotNet.Jobs;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
|
namespace MQTTnet.Benchmarks
|
||||||
|
{
|
||||||
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class AsyncLockBenchmark : BaseBenchmark
|
||||||
|
{
|
||||||
|
[Benchmark]
|
||||||
|
public async Task Synchronize_100_Tasks()
|
||||||
|
{
|
||||||
|
const int tasksCount = 100;
|
||||||
|
|
||||||
|
var tasks = new Task[tasksCount];
|
||||||
|
var asyncLock = new AsyncLock();
|
||||||
|
var globalI = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < tasksCount; i++)
|
||||||
|
{
|
||||||
|
tasks[i] = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (await asyncLock.EnterAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
var localI = globalI;
|
||||||
|
await Task.Delay(5).ConfigureAwait(false); // Increase the chance for wrong data.
|
||||||
|
localI++;
|
||||||
|
globalI = localI;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (globalI != tasksCount)
|
||||||
|
{
|
||||||
|
throw new Exception($"Code is broken ({globalI})!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async Task Wait_100_000_Times()
|
||||||
|
{
|
||||||
|
var asyncLock = new AsyncLock();
|
||||||
|
|
||||||
|
using (var cancellationToken = new CancellationTokenSource())
|
||||||
|
{
|
||||||
|
for (var i = 0; i < 100000; i++)
|
||||||
|
{
|
||||||
|
using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,7 +46,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
|
|
||||||
_stream.Position = 0;
|
_stream.Position = 0;
|
||||||
|
|
||||||
var channel = new TestMqttChannel(_stream);
|
var channel = new MemoryMqttChannel(_stream);
|
||||||
|
|
||||||
_channelAdapter = new MqttChannelAdapter(channel, serializer, null, new MqttNetEventLogger());
|
_channelAdapter = new MqttChannelAdapter(channel, serializer, null, new MqttNetEventLogger());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using MQTTnet.Diagnostics;
|
|||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter]
|
[RPlotExporter]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class LoggerBenchmark
|
public class LoggerBenchmark
|
||||||
|
|||||||
@@ -12,14 +12,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' != 'net5.0'">
|
|
||||||
<PackageReference Include="System.IO.Pipelines" Version="4.7.2" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
||||||
<PackageReference Include="System.IO.Pipelines" Version="5.0.0" />
|
<PackageReference Include="System.IO.Pipelines" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ using BenchmarkDotNet.Attributes;
|
|||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using MqttClient = MQTTnet.Client.MqttClient;
|
|
||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter, RankColumn]
|
[RPlotExporter, RankColumn]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class MessageProcessingBenchmark
|
public class MessageProcessingBenchmark
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using MQTTnet.Diagnostics;
|
|||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class MessageProcessingMqttConnectionContextBenchmark
|
public class MessageProcessingMqttConnectionContextBenchmark
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using MQTTnet.Tests.Mockups;
|
|||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class MqttPacketReaderWriterBenchmark
|
public class MqttPacketReaderWriterBenchmark
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using MQTTnet.Channel;
|
using MQTTnet.Channel;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
@@ -14,7 +15,7 @@ using MQTTnet.Diagnostics;
|
|||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public sealed class MqttTcpChannelBenchmark
|
public sealed class MqttTcpChannelBenchmark
|
||||||
{
|
{
|
||||||
@@ -70,8 +71,8 @@ namespace MQTTnet.Benchmarks
|
|||||||
|
|
||||||
while (read < expected)
|
while (read < expected)
|
||||||
{
|
{
|
||||||
var readresult = await _clientChannel.ReadAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
var readResult = await _clientChannel.ReadAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
||||||
read += readresult;
|
read += readResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,9 +80,11 @@ namespace MQTTnet.Benchmarks
|
|||||||
{
|
{
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
|
|
||||||
|
var buffer = new ArraySegment<byte>(new byte[size]);
|
||||||
|
|
||||||
for (var i = 0; i < iterations; i++)
|
for (var i = 0; i < iterations; i++)
|
||||||
{
|
{
|
||||||
await _serverChannel.WriteAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
await _serverChannel.WriteAsync(buffer, true, CancellationToken.None).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
Console.WriteLine("c = SubscribeBenchmark");
|
Console.WriteLine("c = SubscribeBenchmark");
|
||||||
Console.WriteLine("d = UnsubscribeBenchmark");
|
Console.WriteLine("d = UnsubscribeBenchmark");
|
||||||
Console.WriteLine("e = MessageDeliveryBenchmark");
|
Console.WriteLine("e = MessageDeliveryBenchmark");
|
||||||
|
Console.WriteLine("f = AsyncLockBenchmark");
|
||||||
|
|
||||||
var pressedKey = Console.ReadKey(true);
|
var pressedKey = Console.ReadKey(true);
|
||||||
switch (pressedKey.KeyChar)
|
switch (pressedKey.KeyChar)
|
||||||
@@ -75,6 +76,9 @@ namespace MQTTnet.Benchmarks
|
|||||||
case 'e':
|
case 'e':
|
||||||
BenchmarkRunner.Run<MessageDeliveryBenchmark>();
|
BenchmarkRunner.Run<MessageDeliveryBenchmark>();
|
||||||
break;
|
break;
|
||||||
|
case 'f':
|
||||||
|
BenchmarkRunner.Run<AsyncLockBenchmark>();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using MQTTnet.Tests.Server;
|
|||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter, RankColumn]
|
[RPlotExporter, RankColumn]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class RoundtripProcessingBenchmark
|
public class RoundtripProcessingBenchmark
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ using MQTTnet.Diagnostics;
|
|||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter]
|
[RPlotExporter]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class SerializerBenchmark : BaseBenchmark
|
public class SerializerBenchmark : BaseBenchmark
|
||||||
@@ -104,7 +104,7 @@ namespace MQTTnet.Benchmarks
|
|||||||
return Task.FromResult(count);
|
return Task.FromResult(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using MQTTnet.Tests.Server;
|
|||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter, RankColumn]
|
[RPlotExporter, RankColumn]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class ServerProcessingBenchmark
|
public class ServerProcessingBenchmark
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using MQTTnet.AspNetCore.Client.Tcp;
|
|||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class TcpPipesBenchmark
|
public class TcpPipesBenchmark
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using BenchmarkDotNet.Jobs;
|
|||||||
|
|
||||||
namespace MQTTnet.Benchmarks
|
namespace MQTTnet.Benchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
[RPlotExporter]
|
[RPlotExporter]
|
||||||
[MemoryDiagnoser]
|
[MemoryDiagnoser]
|
||||||
public class TopicFilterComparerBenchmark
|
public class TopicFilterComparerBenchmark
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
Task StartAsync(ManagedMqttClientOptions options);
|
Task StartAsync(ManagedMqttClientOptions options);
|
||||||
|
|
||||||
Task StopAsync();
|
Task StopAsync(bool cleanDisconnect = true);
|
||||||
|
|
||||||
Task SubscribeAsync(ICollection<MqttTopicFilter> topicFilters);
|
Task SubscribeAsync(ICollection<MqttTopicFilter> topicFilters);
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
/// cref="MaintainConnectionAsync" />
|
/// cref="MaintainConnectionAsync" />
|
||||||
/// .
|
/// .
|
||||||
/// </summary>
|
/// </summary>
|
||||||
readonly Dictionary<string, MqttQualityOfServiceLevel> _reconnectSubscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
readonly Dictionary<string, MqttTopicFilter> _reconnectSubscriptions = new Dictionary<string, MqttTopicFilter>();
|
||||||
|
|
||||||
readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
readonly Dictionary<string, MqttTopicFilter> _subscriptions = new Dictionary<string, MqttTopicFilter>();
|
||||||
readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
|
readonly SemaphoreSlim _subscriptionsQueuedSignal = new SemaphoreSlim(0);
|
||||||
readonly HashSet<string> _unsubscriptions = new HashSet<string>();
|
readonly HashSet<string> _unsubscriptions = new HashSet<string>();
|
||||||
|
|
||||||
@@ -62,6 +62,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
CancellationTokenSource _publishingCancellationToken;
|
CancellationTokenSource _publishingCancellationToken;
|
||||||
|
|
||||||
ManagedMqttClientStorageManager _storageManager;
|
ManagedMqttClientStorageManager _storageManager;
|
||||||
|
bool _isCleanDisconnect;
|
||||||
|
|
||||||
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
@@ -160,14 +161,14 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
throw new InvalidOperationException("call StartAsync before publishing messages");
|
throw new InvalidOperationException("call StartAsync before publishing messages");
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage.Topic);
|
MqttTopicValidator.ThrowIfInvalid(applicationMessage.ApplicationMessage);
|
||||||
|
|
||||||
ManagedMqttApplicationMessage removedMessage = null;
|
ManagedMqttApplicationMessage removedMessage = null;
|
||||||
ApplicationMessageSkippedEventArgs applicationMessageSkippedEventArgs = null;
|
ApplicationMessageSkippedEventArgs applicationMessageSkippedEventArgs = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await _messageQueueLock.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
if (_messageQueue.Count >= Options.MaxPendingMessages)
|
||||||
{
|
{
|
||||||
@@ -201,7 +202,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (_applicationMessageSkippedEvent.HasHandlers)
|
if (applicationMessageSkippedEventArgs != null && _applicationMessageSkippedEvent.HasHandlers)
|
||||||
{
|
{
|
||||||
await _applicationMessageSkippedEvent.InvokeAsync(applicationMessageSkippedEventArgs).ConfigureAwait(false);
|
await _applicationMessageSkippedEvent.InvokeAsync(applicationMessageSkippedEventArgs).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -255,10 +256,12 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
_logger.Info("Started");
|
_logger.Info("Started");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StopAsync()
|
public async Task StopAsync(bool cleanDisconnect = true)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
|
_isCleanDisconnect = cleanDisconnect;
|
||||||
|
|
||||||
StopPublishing();
|
StopPublishing();
|
||||||
StopMaintainingConnection();
|
StopMaintainingConnection();
|
||||||
|
|
||||||
@@ -289,14 +292,14 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
foreach (var topicFilter in topicFilters)
|
foreach (var topicFilter in topicFilters)
|
||||||
{
|
{
|
||||||
_subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
|
_subscriptions[topicFilter.Topic] = topicFilter;
|
||||||
_unsubscriptions.Remove(topicFilter.Topic);
|
_unsubscriptions.Remove(topicFilter.Topic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_subscriptionsQueuedSignal.Release();
|
_subscriptionsQueuedSignal.Release();
|
||||||
|
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task UnsubscribeAsync(ICollection<string> topics)
|
public Task UnsubscribeAsync(ICollection<string> topics)
|
||||||
@@ -319,7 +322,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
_subscriptionsQueuedSignal.Release();
|
_subscriptionsQueuedSignal.Release();
|
||||||
|
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
@@ -382,9 +385,12 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.Timeout))
|
if (_isCleanDisconnect)
|
||||||
{
|
{
|
||||||
await InternalClient.DisconnectAsync(new MqttClientDisconnectOptions(), disconnectTimeout.Token).ConfigureAwait(false);
|
using (var disconnectTimeout = new CancellationTokenSource(Options.ClientOptions.Timeout))
|
||||||
|
{
|
||||||
|
await InternalClient.DisconnectAsync(new MqttClientDisconnectOptions(), disconnectTimeout.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
@@ -455,18 +461,11 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
if (_reconnectSubscriptions.Any())
|
if (_reconnectSubscriptions.Any())
|
||||||
{
|
{
|
||||||
var subscriptions = _reconnectSubscriptions.Select(
|
|
||||||
i => new MqttTopicFilter
|
|
||||||
{
|
|
||||||
Topic = i.Key,
|
|
||||||
QualityOfServiceLevel = i.Value
|
|
||||||
});
|
|
||||||
|
|
||||||
var topicFilters = new List<MqttTopicFilter>();
|
var topicFilters = new List<MqttTopicFilter>();
|
||||||
|
|
||||||
foreach (var sub in subscriptions)
|
foreach (var sub in _reconnectSubscriptions)
|
||||||
{
|
{
|
||||||
topicFilters.Add(sub);
|
topicFilters.Add(sub.Value);
|
||||||
|
|
||||||
if (topicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
|
if (topicFilters.Count == Options.MaxTopicFiltersInSubscribeUnsubscribePackets)
|
||||||
{
|
{
|
||||||
@@ -495,14 +494,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
lock (_subscriptions)
|
lock (_subscriptions)
|
||||||
{
|
{
|
||||||
subscriptions = _subscriptions.Select(
|
subscriptions = _subscriptions.Values.ToList();
|
||||||
i => new MqttTopicFilter
|
|
||||||
{
|
|
||||||
Topic = i.Key,
|
|
||||||
QualityOfServiceLevel = i.Value
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
_subscriptions.Clear();
|
_subscriptions.Clear();
|
||||||
|
|
||||||
unsubscriptions = new HashSet<string>(_unsubscriptions);
|
unsubscriptions = new HashSet<string>(_unsubscriptions);
|
||||||
@@ -523,7 +515,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
|
|
||||||
foreach (var subscription in subscriptions)
|
foreach (var subscription in subscriptions)
|
||||||
{
|
{
|
||||||
_reconnectSubscriptions[subscription.Topic] = subscription.QualityOfServiceLevel;
|
_reconnectSubscriptions[subscription.Topic] = subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
var addedTopicFilters = new List<MqttTopicFilter>();
|
var addedTopicFilters = new List<MqttTopicFilter>();
|
||||||
@@ -707,7 +699,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
await InternalClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
|
await InternalClient.PublishAsync(message.ApplicationMessage).ConfigureAwait(false);
|
||||||
|
|
||||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
using (await _messageQueueLock.EnterAsync().ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||||
{
|
{
|
||||||
// While publishing this message, this.PublishAsync could have booted this
|
// While publishing this message, this.PublishAsync could have booted this
|
||||||
// message off the queue to make room for another (when using a cap
|
// message off the queue to make room for another (when using a cap
|
||||||
@@ -738,7 +730,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
//contradict the expected behavior of QoS 1 and 2, that's also true
|
//contradict the expected behavior of QoS 1 and 2, that's also true
|
||||||
//for the usage of a message queue cap, so it's still consistent
|
//for the usage of a message queue cap, so it's still consistent
|
||||||
//with prior behavior in that way.
|
//with prior behavior in that way.
|
||||||
using (await _messageQueueLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
using (await _messageQueueLock.EnterAsync().ConfigureAwait(false)) //lock to avoid conflict with this.PublishAsync
|
||||||
{
|
{
|
||||||
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
_messageQueue.RemoveFirst(i => i.Id.Equals(message.Id));
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
|
||||||
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await _messagesLock.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
_messages.Add(applicationMessage);
|
_messages.Add(applicationMessage);
|
||||||
await SaveAsync().ConfigureAwait(false);
|
await SaveAsync().ConfigureAwait(false);
|
||||||
@@ -45,7 +45,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
|||||||
{
|
{
|
||||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||||
|
|
||||||
using (await _messagesLock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await _messagesLock.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
var index = _messages.IndexOf(applicationMessage);
|
var index = _messages.IndexOf(applicationMessage);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using System;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Extensions.Rpc
|
namespace MQTTnet.Extensions.Rpc
|
||||||
{
|
{
|
||||||
@@ -116,7 +116,7 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
{
|
{
|
||||||
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var awaitable))
|
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out var awaitable))
|
||||||
{
|
{
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if NET452
|
#if NET452
|
||||||
@@ -128,7 +128,7 @@ namespace MQTTnet.Extensions.Rpc
|
|||||||
// Set this message to handled to that other code can avoid execution etc.
|
// Set this message to handled to that other code can avoid execution etc.
|
||||||
eventArgs.IsHandled = true;
|
eventArgs.IsHandled = true;
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -57,15 +57,15 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="WebSocket4Net" Version="0.15.2"/>
|
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj"/>
|
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using System.Threading.Tasks;
|
|||||||
using MQTTnet.Channel;
|
using MQTTnet.Channel;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
|
using MQTTnet.Internal;
|
||||||
using SuperSocket.ClientEngine;
|
using SuperSocket.ClientEngine;
|
||||||
using WebSocket4Net;
|
using WebSocket4Net;
|
||||||
|
|
||||||
@@ -98,7 +99,6 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
#else
|
#else
|
||||||
certificates.Add(certificate);
|
certificates.Add(certificate);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
_webSocket.Close();
|
_webSocket.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
@@ -155,10 +155,10 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
|||||||
return Task.FromResult(readBytes);
|
return Task.FromResult(readBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_webSocket.Send(buffer, offset, count);
|
_webSocket.Send(buffer.Array, buffer.Offset, buffer.Count);
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// 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 MQTTnet.Internal;
|
||||||
|
|
||||||
|
namespace MQTTnet.TestApp
|
||||||
|
{
|
||||||
|
public sealed class AsyncLockTest
|
||||||
|
{
|
||||||
|
public async Task Run()
|
||||||
|
{
|
||||||
|
var asyncLock = new AsyncLock();
|
||||||
|
|
||||||
|
using (var cancellationToken = new CancellationTokenSource())
|
||||||
|
{
|
||||||
|
for (var i = 0; i < 100000; i++)
|
||||||
|
{
|
||||||
|
using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ using System;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
namespace MQTTnet.TestApp
|
||||||
@@ -40,7 +40,7 @@ namespace MQTTnet.TestApp
|
|||||||
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
client.ConnectedAsync += async e =>
|
client.ConnectedAsync += async e =>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||||
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -23,10 +23,4 @@
|
|||||||
<ProjectReference Include="..\..\Source\MQTTnet\MQTTnet.csproj" />
|
<ProjectReference Include="..\..\Source\MQTTnet\MQTTnet.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="Start.bat">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using Newtonsoft.Json;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Extensions.ManagedClient;
|
using MQTTnet.Extensions.ManagedClient;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
namespace MQTTnet.TestApp
|
||||||
@@ -42,7 +42,7 @@ namespace MQTTnet.TestApp
|
|||||||
managedClient.ApplicationMessageReceivedAsync += e =>
|
managedClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Console.WriteLine(">> RECEIVED: " + e.ApplicationMessage.Topic);
|
Console.WriteLine(">> RECEIVED: " + e.ApplicationMessage.Topic);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await managedClient.StartAsync(options);
|
await managedClient.StartAsync(options);
|
||||||
@@ -85,7 +85,7 @@ namespace MQTTnet.TestApp
|
|||||||
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
||||||
{
|
{
|
||||||
File.WriteAllText(Filename, JsonConvert.SerializeObject(messages));
|
File.WriteAllText(Filename, JsonConvert.SerializeObject(messages));
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IList<ManagedMqttApplicationMessage>> LoadQueuedMessagesAsync()
|
public Task<IList<ManagedMqttApplicationMessage>> LoadQueuedMessagesAsync()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
namespace MQTTnet.TestApp
|
||||||
{
|
{
|
||||||
@@ -124,7 +125,7 @@ namespace MQTTnet.TestApp
|
|||||||
{
|
{
|
||||||
_cancellationTokenSource.Cancel();
|
_cancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
return Implementations.PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Cleanup()
|
public async Task Cleanup()
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ namespace MQTTnet.TestApp
|
|||||||
Console.WriteLine("c = Start QoS 0 benchmark");
|
Console.WriteLine("c = Start QoS 0 benchmark");
|
||||||
Console.WriteLine("d = Start server with logging");
|
Console.WriteLine("d = Start server with logging");
|
||||||
Console.WriteLine("e = Start Message Throughput Test");
|
Console.WriteLine("e = Start Message Throughput Test");
|
||||||
|
Console.WriteLine("f = Start AsyncLock Test");
|
||||||
|
|
||||||
var pressedKey = Console.ReadKey(true);
|
var pressedKey = Console.ReadKey(true);
|
||||||
if (pressedKey.KeyChar == '1')
|
if (pressedKey.KeyChar == '1')
|
||||||
@@ -88,6 +89,10 @@ namespace MQTTnet.TestApp
|
|||||||
{
|
{
|
||||||
Task.Run(new MessageThroughputTest().Run);
|
Task.Run(new MessageThroughputTest().Run);
|
||||||
}
|
}
|
||||||
|
else if (pressedKey.KeyChar == 'f')
|
||||||
|
{
|
||||||
|
Task.Run(new AsyncLockTest().Run);
|
||||||
|
}
|
||||||
|
|
||||||
Thread.Sleep(Timeout.Infinite);
|
Thread.Sleep(Timeout.Infinite);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using System.Security.Authentication;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.TestApp
|
namespace MQTTnet.TestApp
|
||||||
@@ -144,7 +144,7 @@ namespace MQTTnet.TestApp
|
|||||||
client.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.ConnectAsync(options);
|
await client.ConnectAsync(options);
|
||||||
|
|||||||
@@ -4,13 +4,11 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Extensions.ManagedClient;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -69,13 +67,13 @@ namespace MQTTnet.TestApp
|
|||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllText(Filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
|
File.WriteAllText(Filename, JsonConvert.SerializeObject(e.StoredRetainedMessages));
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.RetainedMessagesClearedAsync += e =>
|
mqttServer.RetainedMessagesClearedAsync += e =>
|
||||||
{
|
{
|
||||||
File.Delete(Filename);
|
File.Delete(Filename);
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.LoadingRetainedMessageAsync += e =>
|
mqttServer.LoadingRetainedMessageAsync += e =>
|
||||||
@@ -93,7 +91,7 @@ namespace MQTTnet.TestApp
|
|||||||
|
|
||||||
e.LoadedRetainedMessages = retainedMessages;
|
e.LoadedRetainedMessages = retainedMessages;
|
||||||
|
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.InterceptingPublishAsync += e =>
|
mqttServer.InterceptingPublishAsync += e =>
|
||||||
@@ -111,20 +109,20 @@ namespace MQTTnet.TestApp
|
|||||||
e.CloseConnection = true;
|
e.CloseConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.ValidatingConnectionAsync += e =>
|
mqttServer.ValidatingConnectionAsync += e =>
|
||||||
{
|
{
|
||||||
if (e.ClientId == "SpecialClient")
|
if (e.ClientId == "SpecialClient")
|
||||||
{
|
{
|
||||||
if (e.Username != "USER" || e.Password != "PASS")
|
if (e.UserName != "USER" || e.Password != "PASS")
|
||||||
{
|
{
|
||||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.InterceptingSubscriptionAsync += e =>
|
mqttServer.InterceptingSubscriptionAsync += e =>
|
||||||
@@ -140,7 +138,7 @@ namespace MQTTnet.TestApp
|
|||||||
e.CloseConnection = true;
|
e.CloseConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
mqttServer.InterceptingPublishAsync += e =>
|
mqttServer.InterceptingPublishAsync += e =>
|
||||||
@@ -149,7 +147,7 @@ namespace MQTTnet.TestApp
|
|||||||
$"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'",
|
$"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'",
|
||||||
ConsoleColor.Magenta);
|
ConsoleColor.Magenta);
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
//options.ApplicationMessageInterceptor = c =>
|
//options.ApplicationMessageInterceptor = c =>
|
||||||
@@ -177,7 +175,7 @@ namespace MQTTnet.TestApp
|
|||||||
mqttServer.ClientConnectedAsync += e =>
|
mqttServer.ClientConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Console.Write("Client disconnected event fired.");
|
Console.Write("Client disconnected event fired.");
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await mqttServer.StartAsync();
|
await mqttServer.StartAsync();
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
dotnet MQTTnet.TestApp.NetCore.dll
|
|
||||||
@@ -64,7 +64,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
|
|
||||||
await client.ConnectAsync(options, CancellationToken.None).ConfigureAwait(false);
|
await client.ConnectAsync(options, CancellationToken.None).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[ExpectedException(typeof(MqttCommunicationException))]
|
[ExpectedException(typeof(MqttCommunicationException))]
|
||||||
public async Task Connect_To_Wrong_Host()
|
public async Task Connect_To_Wrong_Host()
|
||||||
@@ -80,6 +80,8 @@ namespace MQTTnet.Tests.Client
|
|||||||
{
|
{
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
{
|
{
|
||||||
|
testEnvironment.IgnoreServerLogErrors = true;
|
||||||
|
|
||||||
testEnvironment.ServerPort = 8364;
|
testEnvironment.ServerPort = 8364;
|
||||||
var server = await testEnvironment.StartServer();
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
@@ -116,6 +118,8 @@ namespace MQTTnet.Tests.Client
|
|||||||
{
|
{
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
{
|
{
|
||||||
|
testEnvironment.IgnoreServerLogErrors = true;
|
||||||
|
|
||||||
var server = await testEnvironment.StartServer();
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
using (var lowLevelClient = testEnvironment.CreateLowLevelClient())
|
using (var lowLevelClient = testEnvironment.CreateLowLevelClient())
|
||||||
@@ -123,7 +127,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
Assert.IsFalse(lowLevelClient.IsConnected);
|
Assert.IsFalse(lowLevelClient.IsConnected);
|
||||||
|
|
||||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithTimeout(TimeSpan.FromSeconds(1)).Build();
|
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithTimeout(TimeSpan.FromSeconds(1)).Build();
|
||||||
|
|
||||||
await lowLevelClient.ConnectAsync(clientOptions, CancellationToken.None);
|
await lowLevelClient.ConnectAsync(clientOptions, CancellationToken.None);
|
||||||
|
|
||||||
Assert.IsTrue(lowLevelClient.IsConnected);
|
Assert.IsTrue(lowLevelClient.IsConnected);
|
||||||
@@ -148,7 +152,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsFalse(lowLevelClient.IsConnected);
|
Assert.IsFalse(lowLevelClient.IsConnected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,18 +11,76 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Extensions.ManagedClient;
|
using MQTTnet.Extensions.ManagedClient;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
using MqttClient = MQTTnet.Client.MqttClient;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Client
|
namespace MQTTnet.Tests.Client
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class ManagedMqttClient_Tests : BaseTestClass
|
public sealed class ManagedMqttClient_Tests : BaseTestClass
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Receive_While_Not_Cleanly_Disconnected()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||||
|
|
||||||
|
var senderClient = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
// Prepare managed client.
|
||||||
|
var managedClient = testEnvironment.Factory.CreateManagedMqttClient();
|
||||||
|
await managedClient.SubscribeAsync("#");
|
||||||
|
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(managedClient);
|
||||||
|
|
||||||
|
var managedClientOptions = new ManagedMqttClientOptions
|
||||||
|
{
|
||||||
|
ClientOptions = testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||||
|
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||||
|
.WithClientId(nameof(Receive_While_Not_Cleanly_Disconnected) + "_managed")
|
||||||
|
.WithCleanSession(false)
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
|
||||||
|
await managedClient.StartAsync(managedClientOptions);
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// Send test data.
|
||||||
|
await senderClient.PublishStringAsync("topic1");
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
receivedMessages.AssertReceivedCountEquals(1);
|
||||||
|
|
||||||
|
// Stop the managed client but keep session at server (not clean disconnect required).
|
||||||
|
await managedClient.StopAsync(false);
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// Send new messages in the meantime.
|
||||||
|
await senderClient.PublishStringAsync("topic2", qualityOfServiceLevel: MqttQualityOfServiceLevel.ExactlyOnce);
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// Start the managed client, it should receive the new message.
|
||||||
|
await managedClient.StartAsync(managedClientOptions);
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
receivedMessages.AssertReceivedCountEquals(2);
|
||||||
|
|
||||||
|
// Stop and start again, no new message should be received.
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
await managedClient.StopAsync(false);
|
||||||
|
await LongTestDelay();
|
||||||
|
await managedClient.StartAsync(managedClientOptions);
|
||||||
|
await LongTestDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
receivedMessages.AssertReceivedCountEquals(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Connect_To_Invalid_Server()
|
public async Task Connect_To_Invalid_Server()
|
||||||
{
|
{
|
||||||
@@ -35,11 +93,10 @@ namespace MQTTnet.Tests.Client
|
|||||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient(mqttClient);
|
var managedClient = testEnvironment.Factory.CreateManagedMqttClient(mqttClient);
|
||||||
|
|
||||||
ConnectingFailedEventArgs connectingFailedEventArgs = null;
|
ConnectingFailedEventArgs connectingFailedEventArgs = null;
|
||||||
|
|
||||||
managedClient.ConnectingFailedAsync += args =>
|
managedClient.ConnectingFailedAsync += args =>
|
||||||
{
|
{
|
||||||
connectingFailedEventArgs = args;
|
connectingFailedEventArgs = args;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await managedClient.StartAsync(
|
await managedClient.StartAsync(
|
||||||
@@ -117,7 +174,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receivingClient.ApplicationMessageReceivedAsync += e =>
|
receivingClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await receivingClient.SubscribeAsync("My/last/will");
|
await receivingClient.SubscribeAsync("My/last/will");
|
||||||
@@ -281,7 +338,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Wait a bit for the retained message to be available
|
// Wait a bit for the retained message to be available
|
||||||
await Task.Delay(1000);
|
await LongTestDelay();
|
||||||
|
|
||||||
await sendingClient.DisconnectAsync();
|
await sendingClient.DisconnectAsync();
|
||||||
|
|
||||||
@@ -290,28 +347,29 @@ namespace MQTTnet.Tests.Client
|
|||||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort);
|
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort);
|
||||||
|
|
||||||
var receivedManagedMessages = new List<MqttApplicationMessage>();
|
var receivedManagedMessages = new List<MqttApplicationMessage>();
|
||||||
var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
|
|
||||||
|
var managedClient = testEnvironment.Factory.CreateManagedMqttClient(testEnvironment.CreateClient());
|
||||||
managedClient.ApplicationMessageReceivedAsync += e =>
|
managedClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedManagedMessages.Add(e.ApplicationMessage);
|
receivedManagedMessages.Add(e.ApplicationMessage);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await managedClient.SubscribeAsync("topic");
|
await managedClient.SubscribeAsync("topic");
|
||||||
|
|
||||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
||||||
|
|
||||||
await Task.Delay(1000);
|
await LongTestDelay();
|
||||||
|
|
||||||
Assert.AreEqual(1, receivedManagedMessages.Count);
|
Assert.AreEqual(1, receivedManagedMessages.Count);
|
||||||
|
|
||||||
await managedClient.StopAsync();
|
await managedClient.StopAsync();
|
||||||
|
|
||||||
await Task.Delay(500);
|
await LongTestDelay();
|
||||||
|
|
||||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(1)).Build());
|
||||||
|
|
||||||
await Task.Delay(1000);
|
await LongTestDelay();
|
||||||
|
|
||||||
// After reconnect and then some delay, the retained message must not be received,
|
// After reconnect and then some delay, the retained message must not be received,
|
||||||
// showing that the subscriptions were cleared
|
// showing that the subscriptions were cleared
|
||||||
@@ -319,7 +377,9 @@ namespace MQTTnet.Tests.Client
|
|||||||
|
|
||||||
// Make sure that it gets received after subscribing again.
|
// Make sure that it gets received after subscribing again.
|
||||||
await managedClient.SubscribeAsync("topic");
|
await managedClient.SubscribeAsync("topic");
|
||||||
await Task.Delay(500);
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
Assert.AreEqual(2, receivedManagedMessages.Count);
|
Assert.AreEqual(2, receivedManagedMessages.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,7 +488,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
managedClient.ConnectedAsync += e =>
|
managedClient.ConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
connected.TrySetResult(true);
|
connected.TrySetResult(true);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
return connected.Task;
|
return connected.Task;
|
||||||
@@ -457,7 +517,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
result.TrySetResult(receivedMessages);
|
result.TrySetResult(receivedMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
return result.Task;
|
return result.Task;
|
||||||
@@ -486,7 +546,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
||||||
{
|
{
|
||||||
_messages = messages;
|
_messages = messages;
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,15 +14,17 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Client
|
namespace MQTTnet.Tests.Client
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class Client_Tests : BaseTestClass
|
public sealed class MqttClient_Tests : BaseTestClass
|
||||||
{
|
{
|
||||||
[DataTestMethod]
|
[DataTestMethod]
|
||||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||||
@@ -56,7 +58,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = InvokeInternal();
|
_ = InvokeInternal();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var publishes = Task.WhenAll(publisher.PublishStringAsync("a", null, qos), publisher.PublishStringAsync("b", null, qos));
|
var publishes = Task.WhenAll(publisher.PublishStringAsync("a", null, qos), publisher.PublishStringAsync("b", null, qos));
|
||||||
@@ -107,7 +109,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
client.DisconnectedAsync += args =>
|
client.DisconnectedAsync += args =>
|
||||||
{
|
{
|
||||||
disconnectHandlerCalled = true;
|
disconnectHandlerCalled = true;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build());
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build());
|
||||||
@@ -121,7 +123,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
//Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
|
//Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(100); // disconnected handler is called async
|
await LongTestDelay(); // disconnected handler is called async
|
||||||
Assert.IsTrue(disconnectHandlerCalled);
|
Assert.IsTrue(disconnectHandlerCalled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +138,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
ex = e.Exception;
|
ex = e.Exception;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -145,6 +147,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
@@ -167,7 +170,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
server.InterceptingPublishAsync += c =>
|
server.InterceptingPublishAsync += c =>
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.SendAsync(
|
await client.SendAsync(
|
||||||
@@ -208,7 +211,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
handlerFired = true;
|
handlerFired = true;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
@@ -239,10 +242,10 @@ namespace MQTTnet.Tests.Client
|
|||||||
|
|
||||||
for (var i = 0; i < 98; i++)
|
for (var i = 0; i < 98; i++)
|
||||||
{
|
{
|
||||||
Assert.IsFalse(clients[i].IsConnected, $"clients[{i}] is not connected");
|
Assert.IsFalse(clients[i].TryPingAsync().GetAwaiter().GetResult(), $"clients[{i}] is not connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.IsTrue(clients[99].IsConnected);
|
Assert.IsTrue(clients[99].TryPingAsync().GetAwaiter().GetResult());
|
||||||
|
|
||||||
Assert.AreEqual(1, clientStatus.Count);
|
Assert.AreEqual(1, clientStatus.Count);
|
||||||
Assert.AreEqual(1, sessionStatus.Count);
|
Assert.AreEqual(1, sessionStatus.Count);
|
||||||
@@ -252,7 +255,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receiveClient.ApplicationMessageReceivedAsync += e =>
|
receiveClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await receiveClient.SubscribeAsync("x");
|
await receiveClient.SubscribeAsync("x");
|
||||||
@@ -310,7 +313,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receiver.ApplicationMessageReceivedAsync += e =>
|
receiver.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
||||||
@@ -332,7 +335,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,11 +346,11 @@ namespace MQTTnet.Tests.Client
|
|||||||
{
|
{
|
||||||
await testEnvironment.StartServer();
|
await testEnvironment.StartServer();
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
|
|
||||||
await client.DisconnectAsync();
|
await client.DisconnectAsync();
|
||||||
|
|
||||||
Assert.IsFalse(client.IsConnected);
|
Assert.IsFalse(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,7 +456,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receivedValues.Add(value);
|
receivedValues.Add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
client1.ApplicationMessageReceivedAsync += Handler1;
|
client1.ApplicationMessageReceivedAsync += Handler1;
|
||||||
@@ -531,7 +534,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
client2.ApplicationMessageReceivedAsync += e =>
|
client2.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
|
client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client2.SubscribeAsync(client2Topic);
|
await client2.SubscribeAsync(client2Topic);
|
||||||
@@ -566,7 +569,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receivedMessages.Add(e.ApplicationMessage);
|
receivedMessages.Add(e.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client1.SubscribeAsync("a");
|
await client1.SubscribeAsync("a");
|
||||||
@@ -591,17 +594,17 @@ namespace MQTTnet.Tests.Client
|
|||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(await client.TryPingAsync());
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsFalse(client.IsConnected);
|
Assert.IsFalse(await client.TryPingAsync());
|
||||||
|
|
||||||
await server.StartAsync();
|
await server.StartAsync();
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(await client.TryPingAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,6 +640,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
|
|
||||||
SpinWait.SpinUntil(() => tries >= maxTries, 10000);
|
SpinWait.SpinUntil(() => tries >= maxTries, 10000);
|
||||||
@@ -656,11 +660,11 @@ namespace MQTTnet.Tests.Client
|
|||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
Assert.IsFalse(client.IsConnected);
|
Assert.IsFalse(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
|
|
||||||
for (var i = 0; i < 5; i++)
|
for (var i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
@@ -671,6 +675,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,7 +683,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||||
Assert.IsTrue(client.IsConnected);
|
await client.PingAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,7 +728,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
replies.Add(eventArgs.ApplicationMessage.Topic);
|
replies.Add(eventArgs.ApplicationMessage.Topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
client2.ApplicationMessageReceivedAsync += Handler2;
|
client2.ApplicationMessageReceivedAsync += Handler2;
|
||||||
@@ -761,7 +766,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
replyReceived = true;
|
replyReceived = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
client2.ApplicationMessageReceivedAsync += async e =>
|
client2.ApplicationMessageReceivedAsync += async e =>
|
||||||
@@ -812,7 +817,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
replyReceived = true;
|
replyReceived = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
await client.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
@@ -828,14 +833,14 @@ namespace MQTTnet.Tests.Client
|
|||||||
{
|
{
|
||||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||||
{
|
{
|
||||||
var server = await testEnvironment.StartServer();
|
await testEnvironment.StartServer();
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Assert.IsTrue(e.ClientWasConnected);
|
Assert.IsTrue(e.ClientWasConnected);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.DisconnectAsync();
|
await client.DisconnectAsync();
|
||||||
@@ -851,11 +856,11 @@ namespace MQTTnet.Tests.Client
|
|||||||
var server = await testEnvironment.StartServer();
|
var server = await testEnvironment.StartServer();
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||||
client.DisconnectedAsync += e =>
|
client.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Assert.IsTrue(e.ClientWasConnected);
|
Assert.IsTrue(e.ClientWasConnected);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
@@ -892,7 +897,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
receivedMessages.Add(e.ApplicationMessage);
|
receivedMessages.Add(e.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
|
||||||
@@ -917,14 +922,14 @@ namespace MQTTnet.Tests.Client
|
|||||||
client1.DisconnectedAsync += c =>
|
client1.DisconnectedAsync += c =>
|
||||||
{
|
{
|
||||||
disconnectedFired = true;
|
disconnectedFired = true;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var messageReceived = false;
|
var messageReceived = false;
|
||||||
client1.ApplicationMessageReceivedAsync += e =>
|
client1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
messageReceived = true;
|
messageReceived = true;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("topic1").WithExactlyOnceQoS().Build());
|
await client1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("topic1").WithExactlyOnceQoS().Build());
|
||||||
@@ -941,7 +946,7 @@ namespace MQTTnet.Tests.Client
|
|||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
|
|
||||||
Assert.IsTrue(messageReceived);
|
Assert.IsTrue(messageReceived);
|
||||||
Assert.IsTrue(client1.IsConnected);
|
Assert.IsTrue(client1.TryPingAsync().GetAwaiter().GetResult());
|
||||||
Assert.IsFalse(disconnectedFired);
|
Assert.IsFalse(disconnectedFired);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Collections.Generic;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Diagnostics
|
namespace MQTTnet.Tests.Diagnostics
|
||||||
{
|
{
|
||||||
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests.Diagnostics
|
|||||||
mqttClient.InspectPackage += eventArgs =>
|
mqttClient.InspectPackage += eventArgs =>
|
||||||
{
|
{
|
||||||
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Extensions.ManagedClient;
|
using MQTTnet.Extensions.ManagedClient;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Factory
|
namespace MQTTnet.Tests.Factory
|
||||||
@@ -56,7 +56,7 @@ namespace MQTTnet.Tests.Factory
|
|||||||
managedClient.ConnectingFailedAsync += e =>
|
managedClient.ConnectingFailedAsync += e =>
|
||||||
{
|
{
|
||||||
tcs.TrySetResult(null);
|
tcs.TrySetResult(null);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.WhenAny(Task.Delay(managedClient.Options.ClientOptions.Timeout), tcs.Task);
|
await Task.WhenAny(Task.Delay(managedClient.Options.ClientOptions.Timeout), tcs.Task);
|
||||||
|
|||||||
@@ -13,13 +13,12 @@ using MQTTnet.Adapter;
|
|||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Tests.Extensions;
|
using MQTTnet.Tests.Extensions;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
namespace MQTTnet.Tests.Formatter
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class MqttPacketSerialization_V3_Binary_Tests
|
public sealed class MqttPacketSerialization_V3_Binary_Tests
|
||||||
@@ -563,7 +562,7 @@ namespace MQTTnet.Tests
|
|||||||
|
|
||||||
using (var headerStream = new MemoryStream(buffer1.Join().ToArray()))
|
using (var headerStream = new MemoryStream(buffer1.Join().ToArray()))
|
||||||
{
|
{
|
||||||
using (var channel = new TestMqttChannel(headerStream))
|
using (var channel = new MemoryMqttChannel(headerStream))
|
||||||
{
|
{
|
||||||
using (var adapter = new MqttChannelAdapter(
|
using (var adapter = new MqttChannelAdapter(
|
||||||
channel,
|
channel,
|
||||||
@@ -583,7 +582,7 @@ namespace MQTTnet.Tests
|
|||||||
|
|
||||||
MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter packetFormatterAdapter, byte[] buffer)
|
MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter packetFormatterAdapter, byte[] buffer)
|
||||||
{
|
{
|
||||||
var channel = new TestMqttChannel(buffer);
|
var channel = new MemoryMqttChannel(buffer);
|
||||||
var adapter = new MqttChannelAdapter(channel, packetFormatterAdapter, null, new MqttNetEventLogger());
|
var adapter = new MqttChannelAdapter(channel, packetFormatterAdapter, null, new MqttNetEventLogger());
|
||||||
|
|
||||||
adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||||
@@ -603,7 +602,7 @@ namespace MQTTnet.Tests
|
|||||||
var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, writer);
|
var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, writer);
|
||||||
var buffer = serializer.Encode(packet);
|
var buffer = serializer.Encode(packet);
|
||||||
|
|
||||||
using (var channel = new TestMqttChannel(buffer.Join().ToArray()))
|
using (var channel = new MemoryMqttChannel(buffer.Join().ToArray()))
|
||||||
{
|
{
|
||||||
var adapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)), null, new MqttNetEventLogger());
|
var adapter = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)), null, new MqttNetEventLogger());
|
||||||
return (TPacket)adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
return (TPacket)adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ using MQTTnet.Formatter;
|
|||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
namespace MQTTnet.Tests.Formatter
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class MqttPacketSerialization_V3_Tests
|
public sealed class MqttPacketSerialization_V3_Tests
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using MQTTnet.Formatter;
|
|||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
namespace MQTTnet.Tests.Formatter
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class MqttPacketSerialization_V5_Tests
|
public sealed class MqttPacketSerialization_V5_Tests
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
// 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.Internal;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests.Internal
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class AsyncEvent_Tests
|
||||||
|
{
|
||||||
|
int _testEventAsyncCount;
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Attach_and_Detach_Sync_And_Async_EventHandler()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
|
||||||
|
testClass.TestEventAsync += OnTestEventAsync;
|
||||||
|
testClass.TestEvent += OnTestEvent;
|
||||||
|
|
||||||
|
await testClass.FireEventAsync(EventArgs.Empty);
|
||||||
|
Assert.AreEqual(2, _testEventAsyncCount);
|
||||||
|
|
||||||
|
testClass.TestEvent -= OnTestEvent;
|
||||||
|
|
||||||
|
await testClass.FireEventAsync(EventArgs.Empty);
|
||||||
|
Assert.AreEqual(3, _testEventAsyncCount);
|
||||||
|
|
||||||
|
testClass.TestEventAsync -= OnTestEventAsync;
|
||||||
|
|
||||||
|
await testClass.FireEventAsync(EventArgs.Empty);
|
||||||
|
Assert.AreEqual(3, _testEventAsyncCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Attach_EventHandler()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
|
||||||
|
testClass.TestEventAsync += OnTestEventAsync;
|
||||||
|
|
||||||
|
await testClass.FireEventAsync(EventArgs.Empty);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, _testEventAsyncCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Attach_Sync_And_Async_EventHandler()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
|
||||||
|
testClass.TestEventAsync += OnTestEventAsync;
|
||||||
|
testClass.TestEvent += OnTestEvent;
|
||||||
|
|
||||||
|
await testClass.FireEventAsync(EventArgs.Empty);
|
||||||
|
|
||||||
|
Assert.AreEqual(2, _testEventAsyncCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Detach_EventHandler()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
|
||||||
|
testClass.TestEventAsync += OnTestEventAsync;
|
||||||
|
testClass.TestEventAsync -= OnTestEventAsync;
|
||||||
|
|
||||||
|
await testClass.FireEventAsync(EventArgs.Empty);
|
||||||
|
|
||||||
|
Assert.AreEqual(0, _testEventAsyncCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Has_Handlers()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
testClass.TestEventAsync += OnTestEventAsync;
|
||||||
|
|
||||||
|
Assert.AreEqual(true, testClass.HasTestHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void No_Handlers()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
|
||||||
|
Assert.AreEqual(false, testClass.HasTestHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Remove_Handlers()
|
||||||
|
{
|
||||||
|
var testClass = new TestClass();
|
||||||
|
testClass.TestEventAsync += OnTestEventAsync;
|
||||||
|
testClass.TestEventAsync -= OnTestEventAsync;
|
||||||
|
|
||||||
|
Assert.AreEqual(false, testClass.HasTestHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTestEvent(EventArgs arg)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _testEventAsyncCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
Task OnTestEventAsync(EventArgs arg)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _testEventAsyncCount);
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class TestClass
|
||||||
|
{
|
||||||
|
readonly AsyncEvent<EventArgs> _testEvent = new AsyncEvent<EventArgs>();
|
||||||
|
|
||||||
|
public event Action<EventArgs> TestEvent
|
||||||
|
{
|
||||||
|
add => _testEvent.AddHandler(value);
|
||||||
|
remove => _testEvent.RemoveHandler(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Func<EventArgs, Task> TestEventAsync
|
||||||
|
{
|
||||||
|
add => _testEvent.AddHandler(value);
|
||||||
|
remove => _testEvent.RemoveHandler(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasTestHandlers => _testEvent.HasHandlers;
|
||||||
|
|
||||||
|
public Task FireEventAsync(EventArgs eventArgs)
|
||||||
|
{
|
||||||
|
return _testEvent.InvokeAsync(eventArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
@@ -11,8 +12,186 @@ using MQTTnet.Internal;
|
|||||||
namespace MQTTnet.Tests.Internal
|
namespace MQTTnet.Tests.Internal
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class AsyncLock_Tests
|
public sealed class AsyncLock_Tests
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Cancellation_Of_Awaiter()
|
||||||
|
{
|
||||||
|
var @lock = new AsyncLock();
|
||||||
|
|
||||||
|
// This call will not yet "release" the lock due to missing _using_.
|
||||||
|
var releaser = await @lock.EnterAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
var counter = 0;
|
||||||
|
|
||||||
|
Debug.WriteLine("Prepared locked lock.");
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
// SHOULD GET TIMEOUT!
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 1 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
// SHOULD GET TIMEOUT!
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 2 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
// SHOULD GET TIMEOUT!
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 3 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(4)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 4 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 5 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(6)))
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync(timeout.Token))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Task 6 incremented");
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Debug.WriteLine("Delay before release...");
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(3.1));
|
||||||
|
releaser.Dispose();
|
||||||
|
|
||||||
|
Debug.WriteLine("Wait for all tasks...");
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(6.1));
|
||||||
|
|
||||||
|
Assert.AreEqual(3, counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Lock_Parallel_Tasks()
|
||||||
|
{
|
||||||
|
const int taskCount = 50;
|
||||||
|
|
||||||
|
var @lock = new AsyncLock();
|
||||||
|
|
||||||
|
var tasks = new Task[taskCount];
|
||||||
|
var globalI = 0;
|
||||||
|
for (var i = 0; i < taskCount; i++)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
tasks[i] = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync())
|
||||||
|
{
|
||||||
|
var localI = globalI;
|
||||||
|
await Task.Delay(5); // Increase the chance for wrong data.
|
||||||
|
localI++;
|
||||||
|
globalI = localI;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.WaitAll(tasks);
|
||||||
|
Assert.AreEqual(taskCount, globalI);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Lock_10_Parallel_Tasks_With_Dispose_Doesnt_Lockup()
|
||||||
|
{
|
||||||
|
const int ThreadsCount = 10;
|
||||||
|
|
||||||
|
var threads = new Task[ThreadsCount];
|
||||||
|
var @lock = new AsyncLock();
|
||||||
|
var globalI = 0;
|
||||||
|
for (var i = 0; i < ThreadsCount; i++)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
threads[i] = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
using (await @lock.EnterAsync())
|
||||||
|
{
|
||||||
|
var localI = globalI;
|
||||||
|
await Task.Delay(10); // Increase the chance for wrong data.
|
||||||
|
localI++;
|
||||||
|
globalI = localI;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ContinueWith(
|
||||||
|
x =>
|
||||||
|
{
|
||||||
|
if (globalI == 5)
|
||||||
|
{
|
||||||
|
@lock.Dispose();
|
||||||
|
@lock = new AsyncLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.Exception != null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(x.Exception.GetBaseException().GetType().Name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.WaitAll(threads);
|
||||||
|
|
||||||
|
// Expect only 6 because the others are failing due to disposal (if (globalI == 5)).
|
||||||
|
Assert.AreEqual(6, globalI);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Lock_Serial_Calls()
|
public async Task Lock_Serial_Calls()
|
||||||
{
|
{
|
||||||
@@ -21,7 +200,7 @@ namespace MQTTnet.Tests.Internal
|
|||||||
var @lock = new AsyncLock();
|
var @lock = new AsyncLock();
|
||||||
for (var i = 0; i < 100; i++)
|
for (var i = 0; i < 100; i++)
|
||||||
{
|
{
|
||||||
using (await @lock.WaitAsync(CancellationToken.None).ConfigureAwait(false))
|
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
sum++;
|
sum++;
|
||||||
}
|
}
|
||||||
@@ -37,66 +216,76 @@ namespace MQTTnet.Tests.Internal
|
|||||||
var @lock = new AsyncLock();
|
var @lock = new AsyncLock();
|
||||||
|
|
||||||
// This call will never "release" the lock due to missing _using_.
|
// This call will never "release" the lock due to missing _using_.
|
||||||
await @lock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
|
await @lock.EnterAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
{
|
{
|
||||||
await @lock.WaitAsync(cts.Token).ConfigureAwait(false);
|
await @lock.EnterAsync(cts.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//[TestMethod]
|
|
||||||
//public async Task Test_Cancellation_With_Later_Access()
|
|
||||||
//{
|
|
||||||
// var @lock = new AsyncLock();
|
|
||||||
|
|
||||||
// var releaser = await @lock.WaitAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
|
||||||
// {
|
|
||||||
// await @lock.WaitAsync(cts.Token).ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// catch (OperationCanceledException)
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
|
|
||||||
// releaser.Dispose();
|
|
||||||
|
|
||||||
// using (await @lock.WaitAsync().ConfigureAwait(false))
|
|
||||||
// {
|
|
||||||
// // When the method finished, the thread got access.
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Lock_10_Parallel_Tasks()
|
public async Task Test_Cancellation_With_Later_Access()
|
||||||
{
|
{
|
||||||
const int ThreadsCount = 10;
|
var asyncLock = new AsyncLock();
|
||||||
|
|
||||||
var threads = new Task[ThreadsCount];
|
var releaser = await asyncLock.EnterAsync().ConfigureAwait(false);
|
||||||
var @lock = new AsyncLock();
|
|
||||||
var globalI = 0;
|
try
|
||||||
for (var i = 0; i < ThreadsCount; i++)
|
|
||||||
{
|
{
|
||||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
threads[i] = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
using (var releaser = await @lock.WaitAsync(CancellationToken.None))
|
await asyncLock.EnterAsync(timeout.Token).ConfigureAwait(false);
|
||||||
{
|
}
|
||||||
var localI = globalI;
|
|
||||||
await Task.Delay(10); // Increase the chance for wrong data.
|
Assert.Fail("Exception should be thrown!");
|
||||||
localI++;
|
}
|
||||||
globalI = localI;
|
catch (OperationCanceledException)
|
||||||
}
|
{
|
||||||
});
|
|
||||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.WaitAll(threads);
|
releaser.Dispose();
|
||||||
Assert.AreEqual(ThreadsCount, globalI);
|
|
||||||
|
using (await asyncLock.EnterAsync(CancellationToken.None).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
// When the method finished, the thread got access.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Use_After_Cancellation()
|
||||||
|
{
|
||||||
|
var @lock = new AsyncLock();
|
||||||
|
|
||||||
|
// This call will not yet "release" the lock due to missing _using_.
|
||||||
|
var releaser = await @lock.EnterAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
|
{
|
||||||
|
await @lock.EnterAsync(cts.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// Expected exception!
|
||||||
|
}
|
||||||
|
|
||||||
|
releaser.Dispose();
|
||||||
|
|
||||||
|
// Regular usage after cancellation.
|
||||||
|
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests.Internal
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class AsyncSignal_Tests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(TaskCanceledException))]
|
||||||
|
public async Task Cancel_If_No_Signal()
|
||||||
|
{
|
||||||
|
var asyncSignal = new AsyncSignal();
|
||||||
|
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
|
||||||
|
{
|
||||||
|
await asyncSignal.WaitAsync(timeout.Token);
|
||||||
|
|
||||||
|
Assert.Fail("There is no signal. So we must fail here!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(ObjectDisposedException))]
|
||||||
|
public async Task Dispose_Properly()
|
||||||
|
{
|
||||||
|
var asyncSignal = new AsyncSignal();
|
||||||
|
|
||||||
|
// The timeout will not be reached but another task will kill the async signal.
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(999)))
|
||||||
|
{
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(2000, CancellationToken.None);
|
||||||
|
asyncSignal.Dispose();
|
||||||
|
},
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
await asyncSignal.WaitAsync(timeout.Token);
|
||||||
|
|
||||||
|
Assert.Fail("There is no signal. So we must fail here!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Reset_Signal()
|
||||||
|
{
|
||||||
|
var asyncSignal = new AsyncSignal();
|
||||||
|
|
||||||
|
// WaitAsync should fail because no signal is available.
|
||||||
|
for (var i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100)))
|
||||||
|
{
|
||||||
|
await asyncSignal.WaitAsync(timeout.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Fail("This must fail because the signal is not yet set.");
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
asyncSignal.Set();
|
||||||
|
|
||||||
|
// WaitAsync should return directly because the signal is available.
|
||||||
|
await asyncSignal.WaitAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Signal()
|
||||||
|
{
|
||||||
|
var asyncSignal = new AsyncSignal();
|
||||||
|
|
||||||
|
// The timeout will not be reached but another task will kill the async signal.
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||||
|
{
|
||||||
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(1000, CancellationToken.None);
|
||||||
|
asyncSignal.Set();
|
||||||
|
},
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
await asyncSignal.WaitAsync(timeout.Token);
|
||||||
|
|
||||||
|
stopwatch.Stop();
|
||||||
|
|
||||||
|
Assert.IsTrue(stopwatch.ElapsedMilliseconds > 900);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(InvalidOperationException))]
|
||||||
|
public async Task Fail_For_Two_Waiters()
|
||||||
|
{
|
||||||
|
var asyncSignal = new AsyncSignal();
|
||||||
|
|
||||||
|
// This thread will wait properly because it is the first waiter.
|
||||||
|
_ = Task.Run(
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
await asyncSignal.WaitAsync();
|
||||||
|
},
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
|
// Now the current thread must fail because there is already a waiter.
|
||||||
|
await asyncSignal.WaitAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// 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.Internal;
|
||||||
|
using MQTTnet.Packets;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests.Internal
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class MqttPacketBusItem_Tests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void Fire_Completed_Event()
|
||||||
|
{
|
||||||
|
var eventFired = false;
|
||||||
|
|
||||||
|
var item = new MqttPacketBusItem(new MqttPublishPacket());
|
||||||
|
item.Completed += (_, __) =>
|
||||||
|
{
|
||||||
|
eventFired = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
item.Complete();
|
||||||
|
|
||||||
|
Assert.IsTrue(eventFired);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(TaskCanceledException))]
|
||||||
|
public async Task Wait_Packet_Bus_Item_After_Already_Canceled()
|
||||||
|
{
|
||||||
|
var item = new MqttPacketBusItem(new MqttPublishPacket());
|
||||||
|
|
||||||
|
// Finish the item before the actual
|
||||||
|
item.Cancel();
|
||||||
|
|
||||||
|
await item.WaitAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,69 +13,26 @@ namespace MQTTnet.Tests.Internal
|
|||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class MqttPacketBus_Tests
|
public sealed class MqttPacketBus_Tests
|
||||||
{
|
{
|
||||||
[TestMethod]
|
|
||||||
[ExpectedException(typeof(OperationCanceledException))]
|
|
||||||
public async Task Wait_With_Empty_Bus()
|
|
||||||
{
|
|
||||||
var bus = new MqttPacketBus();
|
|
||||||
|
|
||||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
|
|
||||||
{
|
|
||||||
await bus.DequeueItemAsync(timeout.Token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Fill_From_Different_Task()
|
|
||||||
{
|
|
||||||
const int MessageCount = 500;
|
|
||||||
|
|
||||||
var delayRandom = new Random();
|
|
||||||
|
|
||||||
var bus = new MqttPacketBus();
|
|
||||||
|
|
||||||
_ = Task.Run(
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < MessageCount; i++)
|
|
||||||
{
|
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(MqttPingReqPacket.Instance), MqttPacketBusPartition.Health);
|
|
||||||
|
|
||||||
Thread.Sleep(delayRandom.Next(0, 10));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (var i = 0; i < MessageCount; i++)
|
|
||||||
{
|
|
||||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
|
|
||||||
{
|
|
||||||
await bus.DequeueItemAsync(timeout.Token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.AreEqual(0, bus.TotalItemsCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Alternate_Priorities()
|
public void Alternate_Priorities()
|
||||||
{
|
{
|
||||||
var bus = new MqttPacketBus();
|
var bus = new MqttPacketBus();
|
||||||
|
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||||
|
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||||
|
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||||
|
|
||||||
Assert.AreEqual(9, bus.TotalItemsCount);
|
Assert.AreEqual(9, bus.TotalItemsCount);
|
||||||
|
|
||||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||||
@@ -85,33 +42,10 @@ namespace MQTTnet.Tests.Internal
|
|||||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||||
|
|
||||||
Assert.AreEqual(0, bus.TotalItemsCount);
|
Assert.AreEqual(0, bus.TotalItemsCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Export_Packets_Without_Dequeue()
|
|
||||||
{
|
|
||||||
var bus = new MqttPacketBus();
|
|
||||||
|
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
|
||||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
|
||||||
|
|
||||||
Assert.AreEqual(3, bus.TotalItemsCount);
|
|
||||||
|
|
||||||
var exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Control);
|
|
||||||
Assert.AreEqual(0, exportedPackets.Count);
|
|
||||||
|
|
||||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Health);
|
|
||||||
Assert.AreEqual(0, exportedPackets.Count);
|
|
||||||
|
|
||||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Data);
|
|
||||||
Assert.AreEqual(3, exportedPackets.Count);
|
|
||||||
|
|
||||||
Assert.AreEqual(3, bus.TotalItemsCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Await_Single_Packet()
|
public void Await_Single_Packet()
|
||||||
{
|
{
|
||||||
@@ -121,31 +55,97 @@ namespace MQTTnet.Tests.Internal
|
|||||||
|
|
||||||
var item1 = new MqttPacketBusItem(new MqttPublishPacket());
|
var item1 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||||
var item2 = new MqttPacketBusItem(new MqttPublishPacket());
|
var item2 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||||
|
|
||||||
var item3 = new MqttPacketBusItem(new MqttPublishPacket());
|
var item3 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||||
item3.Delivered += (_, __) =>
|
item3.Completed += (_, __) =>
|
||||||
{
|
{
|
||||||
delivered = true;
|
delivered = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
bus.EnqueueItem(item1, MqttPacketBusPartition.Data);
|
bus.EnqueueItem(item1, MqttPacketBusPartition.Data);
|
||||||
bus.EnqueueItem(item2, MqttPacketBusPartition.Data);
|
bus.EnqueueItem(item2, MqttPacketBusPartition.Data);
|
||||||
bus.EnqueueItem(item3, MqttPacketBusPartition.Data);
|
bus.EnqueueItem(item3, MqttPacketBusPartition.Data);
|
||||||
|
|
||||||
Assert.IsFalse(delivered);
|
Assert.IsFalse(delivered);
|
||||||
|
|
||||||
bus.DequeueItemAsync(CancellationToken.None).Result.MarkAsDelivered();
|
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||||
|
|
||||||
Assert.IsFalse(delivered);
|
Assert.IsFalse(delivered);
|
||||||
|
|
||||||
bus.DequeueItemAsync(CancellationToken.None).Result.MarkAsDelivered();
|
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||||
|
|
||||||
Assert.IsFalse(delivered);
|
Assert.IsFalse(delivered);
|
||||||
|
|
||||||
bus.DequeueItemAsync(CancellationToken.None).Result.MarkAsDelivered();
|
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||||
|
|
||||||
// The third packet has the event attached.
|
// The third packet has the event attached.
|
||||||
Assert.IsTrue(delivered);
|
Assert.IsTrue(delivered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Export_Packets_Without_Dequeue()
|
||||||
|
{
|
||||||
|
var bus = new MqttPacketBus();
|
||||||
|
|
||||||
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||||
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||||
|
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||||
|
|
||||||
|
Assert.AreEqual(3, bus.TotalItemsCount);
|
||||||
|
|
||||||
|
var exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Control);
|
||||||
|
Assert.AreEqual(0, exportedPackets.Count);
|
||||||
|
|
||||||
|
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Health);
|
||||||
|
Assert.AreEqual(0, exportedPackets.Count);
|
||||||
|
|
||||||
|
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Data);
|
||||||
|
Assert.AreEqual(3, exportedPackets.Count);
|
||||||
|
|
||||||
|
Assert.AreEqual(3, bus.TotalItemsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Fill_From_Different_Task()
|
||||||
|
{
|
||||||
|
const int MessageCount = 500;
|
||||||
|
|
||||||
|
var delayRandom = new Random();
|
||||||
|
|
||||||
|
var bus = new MqttPacketBus();
|
||||||
|
|
||||||
|
_ = Task.Run(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
for (var i = 0; i < MessageCount; i++)
|
||||||
|
{
|
||||||
|
bus.EnqueueItem(new MqttPacketBusItem(MqttPingReqPacket.Instance), MqttPacketBusPartition.Health);
|
||||||
|
|
||||||
|
Thread.Sleep(delayRandom.Next(0, 10));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var i = 0; i < MessageCount; i++)
|
||||||
|
{
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
|
||||||
|
{
|
||||||
|
await bus.DequeueItemAsync(timeout.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(0, bus.TotalItemsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(TaskCanceledException))]
|
||||||
|
public async Task Wait_With_Empty_Bus()
|
||||||
|
{
|
||||||
|
var bus = new MqttPacketBus();
|
||||||
|
|
||||||
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
|
||||||
|
{
|
||||||
|
await bus.DequeueItemAsync(timeout.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net452;net461;netcoreapp3.1;net6.0</TargetFrameworks>
|
<TargetFrameworks>net452;net461;net48;netcoreapp3.1;net6.0</TargetFrameworks>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<LangVersion>7.3</LangVersion>
|
<LangVersion>7.3</LangVersion>
|
||||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||||
@@ -10,9 +10,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||||
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Tests.Mockups;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests.MQTTv5
|
namespace MQTTnet.Tests.MQTTv5
|
||||||
{
|
{
|
||||||
@@ -43,7 +42,7 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
client.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.PublishAsync(new MqttApplicationMessageBuilder()
|
await client.PublishAsync(new MqttApplicationMessageBuilder()
|
||||||
@@ -155,7 +154,7 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
var result = await client.PublishStringAsync("a", "b", MqttQualityOfServiceLevel.AtLeastOnce);
|
var result = await client.PublishStringAsync("a", "b", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
await client.DisconnectAsync();
|
await client.DisconnectAsync();
|
||||||
|
|
||||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +169,7 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
var result = await client.PublishStringAsync("a", "b", MqttQualityOfServiceLevel.ExactlyOnce);
|
var result = await client.PublishStringAsync("a", "b", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||||
await client.DisconnectAsync();
|
await client.DisconnectAsync();
|
||||||
|
|
||||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +211,7 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
|
|
||||||
var client1 = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500).WithClientId("client1"));
|
var client1 = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500).WithClientId("client1"));
|
||||||
|
|
||||||
var testMessageHandler = new TestApplicationMessageReceivedHandler(client1);
|
var testMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||||
|
|
||||||
await client1.SubscribeAsync("a");
|
await client1.SubscribeAsync("a");
|
||||||
|
|
||||||
@@ -245,7 +244,7 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
receiver.ApplicationMessageReceivedAsync += e =>
|
receiver.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using MQTTnet.Formatter;
|
|||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.MQTTv5
|
namespace MQTTnet.Tests.MQTTv5
|
||||||
@@ -31,7 +31,7 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||||
@@ -171,11 +171,53 @@ namespace MQTTnet.Tests.MQTTv5
|
|||||||
// Session should be present
|
// Session should be present
|
||||||
|
|
||||||
Assert.IsTrue(!result3.IsSessionPresent, "Session is present when it should not");
|
Assert.IsTrue(!result3.IsSessionPresent, "Session is present when it should not");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MqttClientOptions CreateClientOptions(TestEnvironment testEnvironment, string clientId, bool cleanSession, uint sessionExpiryInterval)
|
[TestMethod]
|
||||||
|
public async Task Disconnect_with_Reason()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var disconnectReason = MqttClientDisconnectReason.UnspecifiedError;
|
||||||
|
|
||||||
|
string testClientId = null;
|
||||||
|
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
testEnvironment.Server.ClientConnectedAsync += e =>
|
||||||
|
{
|
||||||
|
testClientId = e.ClientId;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||||
|
|
||||||
|
client.DisconnectedAsync += e =>
|
||||||
|
{
|
||||||
|
disconnectReason = e.Reason;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// Test client should be connected now
|
||||||
|
|
||||||
|
Assert.IsTrue(testClientId != null);
|
||||||
|
|
||||||
|
// Have the server disconnect the client with AdministrativeAction reason
|
||||||
|
|
||||||
|
await testEnvironment.Server.DisconnectClientAsync(testClientId, MqttDisconnectReasonCode.AdministrativeAction);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// The reason should be returned to the client in the DISCONNECT packet
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttClientDisconnectReason.AdministrativeAction, disconnectReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MqttClientOptions CreateClientOptions(TestEnvironment testEnvironment, string clientId, bool cleanSession, uint sessionExpiryInterval)
|
||||||
{
|
{
|
||||||
return testEnvironment.Factory.CreateClientOptionsBuilder()
|
return testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||||
|
|||||||
+9
-7
@@ -2,24 +2,26 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Channel;
|
using MQTTnet.Channel;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Mockups
|
namespace MQTTnet.Tests.Mockups
|
||||||
{
|
{
|
||||||
public sealed class TestMqttChannel : IMqttChannel
|
public sealed class MemoryMqttChannel : IMqttChannel
|
||||||
{
|
{
|
||||||
readonly MemoryStream _stream;
|
readonly MemoryStream _stream;
|
||||||
|
|
||||||
public TestMqttChannel(MemoryStream stream)
|
public MemoryMqttChannel(MemoryStream stream)
|
||||||
{
|
{
|
||||||
_stream = stream;
|
_stream = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestMqttChannel(byte[] buffer)
|
public MemoryMqttChannel(byte[] buffer)
|
||||||
{
|
{
|
||||||
_stream = new MemoryStream(buffer);
|
_stream = new MemoryStream(buffer);
|
||||||
}
|
}
|
||||||
@@ -32,12 +34,12 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
public Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
@@ -45,9 +47,9 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
return _stream.ReadAsync(buffer, offset, count, cancellationToken);
|
return _stream.ReadAsync(buffer, offset, count, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public Task WriteAsync(ArraySegment<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return _stream.WriteAsync(buffer, offset, count, cancellationToken);
|
return _stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Mockups
|
|
||||||
{
|
|
||||||
public sealed class MqttPacketAsserts
|
|
||||||
{
|
|
||||||
public void AssertIsConnectPacket(MqttPacket packet)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(packet.GetType(), typeof(MqttConnectPacket));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AssertIsConnAckPacket(MqttPacket packet)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(packet.GetType(), typeof(MqttConnAckPacket));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,12 +2,14 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Mockups
|
namespace MQTTnet.Tests.Mockups
|
||||||
{
|
{
|
||||||
@@ -17,7 +19,12 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
|
|
||||||
public TestApplicationMessageReceivedHandler(IMqttClient mqttClient)
|
public TestApplicationMessageReceivedHandler(IMqttClient mqttClient)
|
||||||
{
|
{
|
||||||
mqttClient.ApplicationMessageReceivedAsync += MqttClientOnApplicationMessageReceivedAsync;
|
if (mqttClient == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(mqttClient));
|
||||||
|
}
|
||||||
|
|
||||||
|
mqttClient.ApplicationMessageReceivedAsync += OnApplicationMessageReceivedAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MqttApplicationMessageReceivedEventArgs> ReceivedEventArgs
|
public List<MqttApplicationMessageReceivedEventArgs> ReceivedEventArgs
|
||||||
@@ -39,14 +46,32 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Task MqttClientOnApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
public string GeneratePayloadSequence()
|
||||||
|
{
|
||||||
|
var sequence = new StringBuilder();
|
||||||
|
|
||||||
|
lock (_receivedEventArgs)
|
||||||
|
{
|
||||||
|
foreach (var receivedEventArg in _receivedEventArgs)
|
||||||
|
{
|
||||||
|
var payload = receivedEventArg.ApplicationMessage.ConvertPayloadToString();
|
||||||
|
|
||||||
|
// An empty payload is not part of the sequence!
|
||||||
|
sequence.Append(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sequence.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Task OnApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
lock (_receivedEventArgs)
|
lock (_receivedEventArgs)
|
||||||
{
|
{
|
||||||
_receivedEventArgs.Add(eventArgs);
|
_receivedEventArgs.Add(eventArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,9 +11,10 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Diagnostics;
|
using MQTTnet.Diagnostics;
|
||||||
|
using MQTTnet.Extensions.ManagedClient;
|
||||||
using MQTTnet.Extensions.Rpc;
|
using MQTTnet.Extensions.Rpc;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.LowLevelClient;
|
using MQTTnet.LowLevelClient;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
@@ -109,7 +110,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
configureOptions.Invoke(optionsBuilder);
|
configureOptions.Invoke(optionsBuilder);
|
||||||
|
|
||||||
var options = optionsBuilder.Build();
|
var options = optionsBuilder.Build();
|
||||||
|
|
||||||
var client = CreateClient();
|
var client = CreateClient();
|
||||||
|
|
||||||
if (timeout == TimeSpan.Zero)
|
if (timeout == TimeSpan.Zero)
|
||||||
@@ -137,7 +138,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
options = options.WithTcpServer("127.0.0.1", ServerPort);
|
options = options.WithTcpServer("127.0.0.1", ServerPort);
|
||||||
|
|
||||||
var client = CreateClient();
|
var client = CreateClient();
|
||||||
|
|
||||||
if (timeout == TimeSpan.Zero)
|
if (timeout == TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
await client.ConnectAsync(options.Build()).ConfigureAwait(false);
|
await client.ConnectAsync(options.Build()).ConfigureAwait(false);
|
||||||
@@ -161,7 +162,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
}
|
}
|
||||||
|
|
||||||
var client = CreateClient();
|
var client = CreateClient();
|
||||||
|
|
||||||
if (timeout == TimeSpan.Zero)
|
if (timeout == TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
await client.ConnectAsync(options).ConfigureAwait(false);
|
||||||
@@ -199,6 +200,16 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
return new TestApplicationMessageReceivedHandler(mqttClient);
|
return new TestApplicationMessageReceivedHandler(mqttClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TestApplicationMessageReceivedHandler CreateApplicationMessageHandler(IManagedMqttClient managedClient)
|
||||||
|
{
|
||||||
|
if (managedClient == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(managedClient));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TestApplicationMessageReceivedHandler(managedClient.InternalClient);
|
||||||
|
}
|
||||||
|
|
||||||
public IMqttClient CreateClient()
|
public IMqttClient CreateClient()
|
||||||
{
|
{
|
||||||
lock (_clients)
|
lock (_clients)
|
||||||
@@ -220,7 +231,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
@@ -261,7 +272,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
return Server;
|
return Server;
|
||||||
@@ -325,7 +336,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
var options = optionsBuilder.Build();
|
var options = optionsBuilder.Build();
|
||||||
var server = CreateServer(options);
|
var server = CreateServer(options);
|
||||||
await server.StartAsync();
|
await server.StartAsync();
|
||||||
|
|
||||||
// The OS has chosen the port to we have to properly expose it to the tests.
|
// The OS has chosen the port to we have to properly expose it to the tests.
|
||||||
ServerPort = options.DefaultEndpointOptions.Port;
|
ServerPort = options.DefaultEndpointOptions.Port;
|
||||||
return server;
|
return server;
|
||||||
@@ -344,7 +355,7 @@ namespace MQTTnet.Tests.Mockups
|
|||||||
var options = optionsBuilder.Build();
|
var options = optionsBuilder.Build();
|
||||||
var server = CreateServer(options);
|
var server = CreateServer(options);
|
||||||
await server.StartAsync();
|
await server.StartAsync();
|
||||||
|
|
||||||
// The OS has chosen the port to we have to properly expose it to the tests.
|
// The OS has chosen the port to we have to properly expose it to the tests.
|
||||||
ServerPort = options.DefaultEndpointOptions.Port;
|
ServerPort = options.DefaultEndpointOptions.Port;
|
||||||
return server;
|
return server;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace MQTTnet.Tests
|
|||||||
|
|
||||||
public MqttPacket Decode(MqttPacketBuffer buffer)
|
public MqttPacket Decode(MqttPacketBuffer buffer)
|
||||||
{
|
{
|
||||||
using (var channel = new TestMqttChannel(buffer.ToArray()))
|
using (var channel = new MemoryMqttChannel(buffer.ToArray()))
|
||||||
{
|
{
|
||||||
var formatterAdapter = new MqttPacketFormatterAdapter(_protocolVersion, new MqttBufferWriter(4096, 65535));
|
var formatterAdapter = new MqttPacketFormatterAdapter(_protocolVersion, new MqttBufferWriter(4096, 65535));
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using MQTTnet.Extensions.Rpc;
|
|||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
namespace MQTTnet.Tests
|
||||||
{
|
{
|
||||||
@@ -189,7 +190,7 @@ namespace MQTTnet.Tests
|
|||||||
responseSender.ApplicationMessageReceivedAsync += async e =>
|
responseSender.ApplicationMessageReceivedAsync += async e =>
|
||||||
{
|
{
|
||||||
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
|
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
|
||||||
await PlatformAbstractionLayer.CompletedTask;
|
await CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using System.Diagnostics;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
namespace MQTTnet.Tests
|
namespace MQTTnet.Tests
|
||||||
@@ -33,7 +33,7 @@ namespace MQTTnet.Tests
|
|||||||
receiverClient.ApplicationMessageReceivedAsync += e =>
|
receiverClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
response?.TrySetResult(e.ApplicationMessage.ConvertPayloadToString());
|
response?.TrySetResult(e.ApplicationMessage.ConvertPayloadToString());
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await receiverClient.SubscribeAsync("#");
|
await receiverClient.SubscribeAsync("#");
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
@@ -47,20 +47,20 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.ReasonCode = MqttConnectReasonCode.Success;
|
e.ReasonCode = MqttConnectReasonCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
testEnvironment.Server.ClientConnectedAsync += args =>
|
testEnvironment.Server.ClientConnectedAsync += args =>
|
||||||
{
|
{
|
||||||
serverConnectedClientId = args.ClientId;
|
serverConnectedClientId = args.ClientId;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
testEnvironment.Server.ClientDisconnectedAsync += args =>
|
testEnvironment.Server.ClientDisconnectedAsync += args =>
|
||||||
{
|
{
|
||||||
serverDisconnectedClientId = args.ClientId;
|
serverDisconnectedClientId = args.ClientId;
|
||||||
disconnectedMre.Set();
|
disconnectedMre.Set();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Arrange client
|
// Arrange client
|
||||||
@@ -68,7 +68,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
client.ConnectedAsync += args =>
|
client.ConnectedAsync += args =>
|
||||||
{
|
{
|
||||||
clientAssignedClientId = args.ConnectResult.AssignedClientIdentifier;
|
clientAssignedClientId = args.ConnectResult.AssignedClientIdentifier;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientConnectedAsync += e =>
|
server.ClientConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||||
@@ -54,7 +54,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientDisconnectedAsync += e =>
|
server.ClientDisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||||
@@ -81,7 +81,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientSubscribedTopicAsync += e =>
|
server.ClientSubscribedTopicAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -108,7 +108,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientUnsubscribedTopicAsync += e =>
|
server.ClientUnsubscribedTopicAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -134,7 +134,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.InterceptingPublishAsync += e =>
|
server.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -161,7 +161,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.StartedAsync += e =>
|
server.StartedAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.StartAsync();
|
await server.StartAsync();
|
||||||
@@ -183,7 +183,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.StoppedAsync += e =>
|
server.StoppedAsync += e =>
|
||||||
{
|
{
|
||||||
eventArgs = e;
|
eventArgs = e;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.StopAsync();
|
await server.StopAsync();
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
@@ -99,7 +98,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ValidatingConnectionAsync += e =>
|
server.ValidatingConnectionAsync += e =>
|
||||||
{
|
{
|
||||||
e.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
e.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var connectingFailedException = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(() => testEnvironment.ConnectClient());
|
var connectingFailedException = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(() => testEnvironment.ConnectClient());
|
||||||
@@ -122,7 +121,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.Response.ReasonCode = MqttSubscribeReasonCode.UnspecifiedError;
|
e.Response.ReasonCode = MqttSubscribeReasonCode.UnspecifiedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prepare some retained messages.
|
// Prepare some retained messages.
|
||||||
@@ -146,7 +145,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
buffer.Append(e.ApplicationMessage.Topic);
|
buffer.Append(e.ApplicationMessage.Topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client2.SubscribeAsync("y");
|
await client2.SubscribeAsync("y");
|
||||||
@@ -172,13 +171,13 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientConnectedAsync += e =>
|
server.ClientConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref clientConnectedCalled);
|
Interlocked.Increment(ref clientConnectedCalled);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
server.ClientDisconnectedAsync += e =>
|
server.ClientDisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref clientDisconnectedCalled);
|
Interlocked.Increment(ref clientDisconnectedCalled);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder());
|
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder());
|
||||||
@@ -225,7 +224,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
// Clear retained message.
|
// Clear retained message.
|
||||||
await client.PublishAsync(
|
await client.PublishAsync(
|
||||||
new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
||||||
.WithPayload(PlatformAbstractionLayer.EmptyByteArray)
|
.WithPayload(EmptyBuffer.Array)
|
||||||
.WithRetainFlag()
|
.WithRetainFlag()
|
||||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
.Build());
|
.Build());
|
||||||
@@ -276,7 +275,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
{
|
{
|
||||||
e.ApplicationMessage = new MqttApplicationMessage { Topic = "new_topic" };
|
e.ApplicationMessage = new MqttApplicationMessage { Topic = "new_topic" };
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
string receivedTopic = null;
|
string receivedTopic = null;
|
||||||
@@ -286,7 +285,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += a =>
|
c1.ApplicationMessageReceivedAsync += a =>
|
||||||
{
|
{
|
||||||
receivedTopic = a.ApplicationMessage.Topic;
|
receivedTopic = a.ApplicationMessage.Topic;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("original_topic").Build());
|
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("original_topic").Build());
|
||||||
@@ -305,7 +304,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.InterceptingPublishAsync += e =>
|
server.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
e.ApplicationMessage.Payload = Encoding.ASCII.GetBytes("extended");
|
e.ApplicationMessage.Payload = Encoding.ASCII.GetBytes("extended");
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var c1 = await testEnvironment.ConnectClient();
|
var c1 = await testEnvironment.ConnectClient();
|
||||||
@@ -316,7 +315,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c2.ApplicationMessageReceivedAsync += e =>
|
c2.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
isIntercepted = string.Compare("extended", Encoding.UTF8.GetString(e.ApplicationMessage.Payload), StringComparison.Ordinal) == 0;
|
isIntercepted = string.Compare("extended", Encoding.UTF8.GetString(e.ApplicationMessage.Payload), StringComparison.Ordinal) == 0;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("test").Build());
|
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("test").Build());
|
||||||
@@ -339,7 +338,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ApplicationMessageNotConsumedAsync += e =>
|
server.ApplicationMessageNotConsumedAsync += e =>
|
||||||
{
|
{
|
||||||
undeliverd = e.ApplicationMessage.Topic;
|
undeliverd = e.ApplicationMessage.Topic;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -376,7 +375,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
receivedMessages.Add(e.ApplicationMessage);
|
receivedMessages.Add(e.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
@@ -400,7 +399,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
s.RetainedMessageChangedAsync += e =>
|
s.RetainedMessageChangedAsync += e =>
|
||||||
{
|
{
|
||||||
savedRetainedMessages = e.StoredRetainedMessages;
|
savedRetainedMessages = e.StoredRetainedMessages;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var c1 = await testEnvironment.ConnectClient();
|
var c1 = await testEnvironment.ConnectClient();
|
||||||
@@ -440,7 +439,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedTopic = e.ApplicationMessage.Topic;
|
receivedTopic = e.ApplicationMessage.Topic;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync("#");
|
await c1.SubscribeAsync("#");
|
||||||
@@ -487,7 +486,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
client.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce).Build();
|
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce).Build();
|
||||||
@@ -521,13 +520,13 @@ namespace MQTTnet.Tests.Server
|
|||||||
c2.ApplicationMessageReceivedAsync += e =>
|
c2.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
c3.ApplicationMessageReceivedAsync += e =>
|
c3.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }).ConfigureAwait(false);
|
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }).ConfigureAwait(false);
|
||||||
@@ -581,7 +580,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("c");
|
events.Add("c");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
server.ClientDisconnectedAsync += e =>
|
server.ClientDisconnectedAsync += e =>
|
||||||
@@ -591,7 +590,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("d");
|
events.Add("d");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var clientOptionsBuilder = new MqttClientOptionsBuilder().WithClientId(Guid.NewGuid().ToString());
|
var clientOptionsBuilder = new MqttClientOptionsBuilder().WithClientId(Guid.NewGuid().ToString());
|
||||||
@@ -621,7 +620,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("r");
|
events.Add("r");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c2.SubscribeAsync("topic");
|
await c2.SubscribeAsync("topic");
|
||||||
@@ -669,7 +668,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ValidatingConnectionAsync += e =>
|
server.ValidatingConnectionAsync += e =>
|
||||||
{
|
{
|
||||||
ConnectionValidationHandler(e);
|
ConnectionValidationHandler(e);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var events = new List<string>();
|
var events = new List<string>();
|
||||||
@@ -681,7 +680,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("c");
|
events.Add("c");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
server.ClientDisconnectedAsync += e =>
|
server.ClientDisconnectedAsync += e =>
|
||||||
@@ -691,7 +690,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("d");
|
events.Add("d");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var clientOptions = new MqttClientOptionsBuilder().WithClientId("same_id");
|
var clientOptions = new MqttClientOptionsBuilder().WithClientId("same_id");
|
||||||
@@ -706,7 +705,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("x");
|
events.Add("x");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
@@ -716,7 +715,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
events.Add("r");
|
events.Add("r");
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
c1.SubscribeAsync("topic").Wait();
|
c1.SubscribeAsync("topic").Wait();
|
||||||
@@ -785,7 +784,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
client1.ApplicationMessageReceivedAsync += e =>
|
client1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedBody = e.ApplicationMessage.Payload;
|
receivedBody = e.ApplicationMessage.Payload;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client1.SubscribeAsync("string");
|
await client1.SubscribeAsync("string");
|
||||||
@@ -798,38 +797,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
Assert.IsTrue(longBody.SequenceEqual(receivedBody ?? new byte[0]));
|
Assert.IsTrue(longBody.SequenceEqual(receivedBody ?? new byte[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Session_Takeover()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
|
||||||
{
|
|
||||||
await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
var options = new MqttClientOptionsBuilder().WithCleanSession(false)
|
|
||||||
.WithProtocolVersion(MqttProtocolVersion.V500) // Disconnect reason is only available in MQTT 5+
|
|
||||||
.WithClientId("a");
|
|
||||||
|
|
||||||
var client1 = await testEnvironment.ConnectClient(options);
|
|
||||||
await Task.Delay(500);
|
|
||||||
|
|
||||||
var disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
|
|
||||||
client1.DisconnectedAsync += c =>
|
|
||||||
{
|
|
||||||
disconnectReason = c.Reason;
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
|
||||||
};
|
|
||||||
|
|
||||||
var client2 = await testEnvironment.ConnectClient(options);
|
|
||||||
await Task.Delay(500);
|
|
||||||
|
|
||||||
Assert.IsFalse(client1.IsConnected);
|
|
||||||
Assert.IsTrue(client2.IsConnected);
|
|
||||||
|
|
||||||
Assert.AreEqual(MqttClientDisconnectReason.SessionTakenOver, disconnectReason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Set_Subscription_At_Server()
|
public async Task Set_Subscription_At_Server()
|
||||||
{
|
{
|
||||||
@@ -853,7 +821,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
receivedMessages.Add(e.ApplicationMessage);
|
receivedMessages.Add(e.ApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
@@ -881,7 +849,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.DisconnectedAsync += e =>
|
c1.DisconnectedAsync += e =>
|
||||||
{
|
{
|
||||||
disconnectCalled++;
|
disconnectCalled++;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
@@ -944,26 +912,6 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Use_Clean_Session()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
|
||||||
{
|
|
||||||
await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
var client = testEnvironment.CreateClient();
|
|
||||||
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).WithCleanSession().Build());
|
|
||||||
|
|
||||||
// Create the session including the subscription.
|
|
||||||
var client1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("a").WithCleanSession(false));
|
|
||||||
await client1.SubscribeAsync("x");
|
|
||||||
await client1.DisconnectAsync();
|
|
||||||
await Task.Delay(500);
|
|
||||||
|
|
||||||
Assert.IsFalse(connectResult.IsSessionPresent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Use_Empty_Client_ID()
|
public async Task Use_Empty_Client_ID()
|
||||||
{
|
{
|
||||||
@@ -988,93 +936,57 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Will_Message_Do_Not_Send_On_Clean_Disconnect()
|
public async Task Disconnect_Client_with_Reason()
|
||||||
{
|
{
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
{
|
{
|
||||||
var receivedMessagesCount = 0;
|
var disconnectPacketReceived = false;
|
||||||
|
|
||||||
|
string testClientId = null;
|
||||||
|
|
||||||
await testEnvironment.StartServer();
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will");
|
testEnvironment.Server.ClientConnectedAsync += e =>
|
||||||
|
|
||||||
var c1 = await testEnvironment.ConnectClient();
|
|
||||||
|
|
||||||
c1.ApplicationMessageReceivedAsync += e =>
|
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
testClientId = e.ClientId;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
var client = testEnvironment.CreateClient();
|
||||||
|
|
||||||
var c2 = await testEnvironment.ConnectClient(clientOptions);
|
client.InspectPackage += e =>
|
||||||
await c2.DisconnectAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
await Task.Delay(1000);
|
|
||||||
|
|
||||||
Assert.AreEqual(0, receivedMessagesCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Will_Message_Do_Not_Send_On_Takeover()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
|
||||||
{
|
|
||||||
var receivedMessagesCount = 0;
|
|
||||||
|
|
||||||
await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
// C1 will receive the last will!
|
|
||||||
var c1 = await testEnvironment.ConnectClient();
|
|
||||||
c1.ApplicationMessageReceivedAsync += e =>
|
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
if (e.Buffer.Length > 0)
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
{
|
||||||
|
if (e.Buffer[0] == (byte)MqttControlPacketType.Disconnect << 4)
|
||||||
|
{
|
||||||
|
disconnectPacketReceived = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
await client.ConnectAsync(
|
||||||
|
new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort)
|
||||||
|
.WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311)
|
||||||
|
.Build()
|
||||||
|
);
|
||||||
|
|
||||||
// C2 has the last will defined.
|
await LongTestDelay();
|
||||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithClientId("WillOwner");
|
|
||||||
|
|
||||||
var c2 = await testEnvironment.ConnectClient(clientOptions);
|
// Test client should be connected now
|
||||||
|
|
||||||
// C3 will do the connection takeover.
|
Assert.IsTrue(testClientId != null);
|
||||||
var c3 = await testEnvironment.ConnectClient(clientOptions);
|
|
||||||
|
|
||||||
await Task.Delay(1000);
|
// Have the server disconnect the client with SessionTakenOver reason; no DISCONNECT should be sent to MQTT 3.1.1 clients
|
||||||
|
|
||||||
Assert.AreEqual(0, receivedMessagesCount);
|
await testEnvironment.Server.DisconnectClientAsync(testClientId, Protocol.MqttDisconnectReasonCode.SessionTakenOver);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
await LongTestDelay();
|
||||||
public async Task Will_Message_Send()
|
|
||||||
{
|
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
|
||||||
{
|
|
||||||
await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder());
|
// MQTT 3.1.1 client should not receive a disconnect packet
|
||||||
|
|
||||||
var receivedMessagesCount = 0;
|
Assert.IsFalse(disconnectPacketReceived, "Disconnect packet received when none is expected");
|
||||||
c1.ApplicationMessageReceivedAsync += e =>
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
|
||||||
};
|
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
|
||||||
|
|
||||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce);
|
|
||||||
var c2 = await testEnvironment.ConnectClient(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
receiverClient.ApplicationMessageReceivedAsync += e =>
|
receiverClient.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessages);
|
Interlocked.Increment(ref receivedMessages);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await receiverClient.SubscribeAsync("t/+");
|
await receiverClient.SubscribeAsync("t/+");
|
||||||
@@ -55,7 +55,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(60));
|
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(120));
|
||||||
|
|
||||||
Assert.AreEqual(100000, receivedMessages);
|
Assert.AreEqual(100000, receivedMessages);
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.InterceptingPublishAsync += e =>
|
server.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessages);
|
Interlocked.Increment(ref receivedMessages);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var i = 0; i < 100; i++)
|
for (var i = 0; i < 100; i++)
|
||||||
@@ -137,7 +137,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.InterceptingPublishAsync += e =>
|
server.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessages);
|
Interlocked.Increment(ref receivedMessages);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var i = 0; i < 100; i++)
|
for (var i = 0; i < 100; i++)
|
||||||
|
|||||||
@@ -130,9 +130,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
|
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
|
||||||
{
|
{
|
||||||
ulong topicHashMask; // not needed
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out _, out _);
|
||||||
bool hasWildcard; // not needed
|
|
||||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out topicHashMask, out hasWildcard);
|
|
||||||
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
|
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Formatter;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests.Server
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class Publish_Tests : BaseTestClass
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Return_Success_When_Subscribed()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var sender = await testEnvironment.ConnectClient();
|
||||||
|
var receiver = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
await receiver.SubscribeAsync("A");
|
||||||
|
|
||||||
|
// AtLeastOnce is required to get an ACK packet from the server.
|
||||||
|
var publishResult = await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttClientPublishReasonCode.Success, publishResult.ReasonCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(true, publishResult.IsSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Return_NoMatchingSubscribers_When_Not_Subscribed()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var sender = await testEnvironment.ConnectClient();
|
||||||
|
var receiver = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
await receiver.SubscribeAsync("A");
|
||||||
|
|
||||||
|
// AtLeastOnce is required to get an ACK packet from the server.
|
||||||
|
var publishResult = await sender.PublishStringAsync("B", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, publishResult.ReasonCode);
|
||||||
|
|
||||||
|
Assert.AreEqual(true, publishResult.IsSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// 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.Protocol;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests.Server
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class Publishing_Tests : BaseTestClass
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||||
|
public async Task Disconnect_While_Publishing()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
// The client will be disconnect directly after subscribing!
|
||||||
|
server.InterceptingPublishAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
await client.PublishStringAsync("test", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
// 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.Tasks;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests.Server
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class QoS_Tests : BaseTestClass
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Preserve_Message_Order_For_Queued_Messages()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||||
|
|
||||||
|
// Create a session which will contain the messages.
|
||||||
|
var dummyClient = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
|
||||||
|
await dummyClient.SubscribeAsync("#", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
dummyClient.Dispose();
|
||||||
|
|
||||||
|
// Now inject messages which are appended to the queue of the client.
|
||||||
|
await server.InjectApplicationMessage("T", "0", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await server.InjectApplicationMessage("T", "2", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
await server.InjectApplicationMessage("T", "1", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await server.InjectApplicationMessage("T", "4", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
await server.InjectApplicationMessage("T", "3", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await server.InjectApplicationMessage("T", "6", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
await server.InjectApplicationMessage("T", "5", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await server.InjectApplicationMessage("T", "8", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
await server.InjectApplicationMessage("T", "7", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await server.InjectApplicationMessage("T", "9", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// Create a new client for the existing message.
|
||||||
|
var client = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
|
||||||
|
var messages = testEnvironment.CreateApplicationMessageHandler(client);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
var payloadSequence = messages.GeneratePayloadSequence();
|
||||||
|
Assert.AreEqual("0214365879", payloadSequence);
|
||||||
|
|
||||||
|
// Disconnect and reconnect to make sure that the server will not send the messages twice.
|
||||||
|
await client.DisconnectAsync();
|
||||||
|
await LongTestDelay();
|
||||||
|
await client.ReconnectAsync();
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
payloadSequence = messages.GeneratePayloadSequence();
|
||||||
|
Assert.AreEqual("0214365879", payloadSequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Fire_Event_On_Client_Acknowledges_QoS_0()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
ClientAcknowledgedPublishPacketEventArgs eventArgs = null;
|
||||||
|
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||||
|
{
|
||||||
|
eventArgs = args;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client1 = await testEnvironment.ConnectClient();
|
||||||
|
await client1.SubscribeAsync("A");
|
||||||
|
|
||||||
|
var client2 = await testEnvironment.ConnectClient();
|
||||||
|
await client2.PublishStringAsync("A");
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// Must be null because no event should be fired for QoS 0.
|
||||||
|
Assert.IsNull(eventArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Fire_Event_On_Client_Acknowledges_QoS_1()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
ClientAcknowledgedPublishPacketEventArgs eventArgs = null;
|
||||||
|
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||||
|
{
|
||||||
|
eventArgs = args;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client1 = await testEnvironment.ConnectClient();
|
||||||
|
await client1.SubscribeAsync("A", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
var client2 = await testEnvironment.ConnectClient();
|
||||||
|
await client2.PublishStringAsync("A", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
Assert.IsNotNull(eventArgs);
|
||||||
|
Assert.IsNotNull(eventArgs.PublishPacket);
|
||||||
|
Assert.IsNotNull(eventArgs.AcknowledgePacket);
|
||||||
|
Assert.IsTrue(eventArgs.IsCompleted);
|
||||||
|
|
||||||
|
Assert.AreEqual("A", eventArgs.PublishPacket.Topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Fire_Event_On_Client_Acknowledges_QoS_2()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var eventArgs = new List<ClientAcknowledgedPublishPacketEventArgs>();
|
||||||
|
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||||
|
{
|
||||||
|
lock (eventArgs)
|
||||||
|
{
|
||||||
|
eventArgs.Add(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client1 = await testEnvironment.ConnectClient();
|
||||||
|
await client1.SubscribeAsync("A", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||||
|
|
||||||
|
var client2 = await testEnvironment.ConnectClient();
|
||||||
|
await client2.PublishStringAsync("A", qualityOfServiceLevel: MqttQualityOfServiceLevel.ExactlyOnce);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
Assert.AreEqual(1, eventArgs.Count);
|
||||||
|
|
||||||
|
var firstEvent = eventArgs[0];
|
||||||
|
|
||||||
|
Assert.IsNotNull(firstEvent);
|
||||||
|
Assert.IsNotNull(firstEvent.PublishPacket);
|
||||||
|
Assert.IsNotNull(firstEvent.AcknowledgePacket);
|
||||||
|
Assert.IsTrue(firstEvent.IsCompleted);
|
||||||
|
|
||||||
|
Assert.AreEqual("A", firstEvent.PublishPacket.Topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,6 +65,66 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Downgrade_QoS_Level()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var c1 = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
// Add the retained message with QoS 2!
|
||||||
|
await c1.PublishAsync(
|
||||||
|
new MqttApplicationMessageBuilder().WithTopic("retained")
|
||||||
|
.WithPayload(new byte[3])
|
||||||
|
.WithRetainFlag()
|
||||||
|
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
|
||||||
|
.Build());
|
||||||
|
|
||||||
|
// The second client uses QoS 1 so a downgrade is required.
|
||||||
|
var c2 = await testEnvironment.ConnectClient();
|
||||||
|
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||||
|
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
messageHandler.AssertReceivedCountEquals(1);
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task No_Upgrade_QoS_Level()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var c1 = await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
// Add the retained message with QoS 1!
|
||||||
|
await c1.PublishAsync(
|
||||||
|
new MqttApplicationMessageBuilder().WithTopic("retained")
|
||||||
|
.WithPayload(new byte[3])
|
||||||
|
.WithRetainFlag()
|
||||||
|
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
|
.Build());
|
||||||
|
|
||||||
|
// The second client uses QoS 2 so an upgrade is expected but according to the MQTT spec this is not supported!
|
||||||
|
var c2 = await testEnvironment.ConnectClient();
|
||||||
|
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||||
|
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
messageHandler.AssertReceivedCountEquals(1);
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Receive_No_Retained_Message_After_Subscribe()
|
public async Task Receive_No_Retained_Message_After_Subscribe()
|
||||||
{
|
{
|
||||||
@@ -124,7 +184,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
.WithRetainFlag()
|
.WithRetainFlag()
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
await c1.DisconnectAsync();
|
await c1.DisconnectAsync();
|
||||||
|
|
||||||
// Subscribe using a new client.
|
// Subscribe using a new client.
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Adapter;
|
using MQTTnet.Adapter;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Exceptions;
|
using MQTTnet.Exceptions;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
@@ -15,6 +16,104 @@ namespace MQTTnet.Tests.Server
|
|||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class Security_Tests : BaseTestClass
|
public sealed class Security_Tests : BaseTestClass
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Do_Not_Affect_Authorized_Clients()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
testEnvironment.IgnoreClientLogErrors = true;
|
||||||
|
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var publishedApplicationMessages = new List<MqttApplicationMessage>();
|
||||||
|
testEnvironment.Server.InterceptingPublishAsync += eventArgs =>
|
||||||
|
{
|
||||||
|
lock (publishedApplicationMessages)
|
||||||
|
{
|
||||||
|
publishedApplicationMessages.Add(eventArgs.ApplicationMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
testEnvironment.Server.ValidatingConnectionAsync += eventArgs =>
|
||||||
|
{
|
||||||
|
if (eventArgs.UserName == "SECRET")
|
||||||
|
{
|
||||||
|
eventArgs.ReasonCode = MqttConnectReasonCode.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eventArgs.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var validClient = testEnvironment.CreateClient())
|
||||||
|
{
|
||||||
|
await validClient.ConnectAsync(
|
||||||
|
testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||||
|
.WithTcpServer("localhost", testEnvironment.ServerPort)
|
||||||
|
.WithCredentials("SECRET")
|
||||||
|
.WithClientId("CLIENT")
|
||||||
|
.Build());
|
||||||
|
|
||||||
|
await validClient.PublishStringAsync("HELLO 1");
|
||||||
|
|
||||||
|
// The following code tries to connect a new client with the same client ID but invalid
|
||||||
|
// credentials. This should block the second client but keep the first one up and running.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var invalidClient = testEnvironment.CreateClient())
|
||||||
|
{
|
||||||
|
await invalidClient.ConnectAsync(
|
||||||
|
testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||||
|
.WithTcpServer("localhost", testEnvironment.ServerPort)
|
||||||
|
.WithCredentials("???")
|
||||||
|
.WithClientId("CLIENT")
|
||||||
|
.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore errors from the second client.
|
||||||
|
}
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
await validClient.PublishStringAsync("HELLO 2");
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
await validClient.PublishStringAsync("HELLO 3");
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
Assert.AreEqual(3, publishedApplicationMessages.Count);
|
||||||
|
Assert.AreEqual(1, testEnvironment.Server.GetClientsAsync().GetAwaiter().GetResult().Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public Task Handle_Wrong_Password()
|
||||||
|
{
|
||||||
|
return TestCredentials("UserName", "x");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public Task Handle_Wrong_UserName()
|
||||||
|
{
|
||||||
|
return TestCredentials("x", "Password1");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public Task Handle_Wrong_UserName_And_Password()
|
||||||
|
{
|
||||||
|
return TestCredentials("x", "x");
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Use_Username_Null_Password_Empty()
|
public async Task Use_Username_Null_Password_Empty()
|
||||||
{
|
{
|
||||||
@@ -29,10 +128,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
var client = testEnvironment.CreateClient();
|
var client = testEnvironment.CreateClient();
|
||||||
|
|
||||||
var clientOptions = new MqttClientOptionsBuilder()
|
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(username, password).Build();
|
||||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
|
||||||
.WithCredentials(username, password)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions));
|
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions));
|
||||||
Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException));
|
Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException));
|
||||||
@@ -41,25 +137,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
async Task TestCredentials(string userName, string password)
|
||||||
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())
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
{
|
{
|
||||||
@@ -69,7 +147,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
server.ValidatingConnectionAsync += e =>
|
server.ValidatingConnectionAsync += e =>
|
||||||
{
|
{
|
||||||
if (e.Username != "UserName1")
|
if (e.UserName != "UserName1")
|
||||||
{
|
{
|
||||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||||
}
|
}
|
||||||
@@ -79,15 +157,12 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = testEnvironment.CreateClient();
|
var client = testEnvironment.CreateClient();
|
||||||
|
|
||||||
var clientOptions = new MqttClientOptionsBuilder()
|
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(userName, password).Build();
|
||||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
|
||||||
.WithCredentials(userName, password)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions));
|
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions));
|
||||||
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, ex.Result.ResultCode);
|
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, ex.Result.ResultCode);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using MQTTnet.Adapter;
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
@@ -28,7 +29,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
{
|
{
|
||||||
e.ReasonCode = MqttConnectReasonCode.ServerMoved;
|
e.ReasonCode = MqttConnectReasonCode.ServerMoved;
|
||||||
e.ServerReference = "new_server";
|
e.ServerReference = "new_server";
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
using MqttClient = MQTTnet.Client.MqttClient;
|
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
{
|
{
|
||||||
@@ -30,7 +28,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||||
|
|
||||||
const string ClientId = "Client1";
|
const string clientId = "Client1";
|
||||||
|
|
||||||
// Create client with clean session and long session expiry interval
|
// Create client with clean session and long session expiry interval
|
||||||
|
|
||||||
@@ -39,7 +37,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||||
.WithCleanSession() // start and end with clean session
|
.WithCleanSession() // start and end with clean session
|
||||||
.WithClientId(ClientId)
|
.WithClientId(clientId)
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
// Disconnect; empty session should be removed from server
|
// Disconnect; empty session should be removed from server
|
||||||
@@ -58,7 +56,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||||
.WithCleanSession(false) // see if there is a session
|
.WithCleanSession(false) // see if there is a session
|
||||||
.WithClientId(ClientId)
|
.WithClientId(clientId)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
@@ -84,7 +82,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.SessionDeletedAsync += e =>
|
server.SessionDeletedAsync += e =>
|
||||||
{
|
{
|
||||||
deletedEventFired = true;
|
deletedEventFired = true;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -112,7 +110,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.SessionItems["can_subscribe_x"] = true;
|
e.SessionItems["can_subscribe_x"] = true;
|
||||||
e.SessionItems["default_payload"] = "Hello World";
|
e.SessionItems["default_payload"] = "Hello World";
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await testEnvironment.ConnectClient();
|
await testEnvironment.ConnectClient();
|
||||||
@@ -124,8 +122,12 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NET452
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Handle_Parallel_Connection_Attempts()
|
[DataRow(MqttProtocolVersion.V311)]
|
||||||
|
[DataRow(MqttProtocolVersion.V500)]
|
||||||
|
public async Task Handle_Parallel_Connection_Attempts(MqttProtocolVersion protocolVersion)
|
||||||
{
|
{
|
||||||
using (var testEnvironment = CreateTestEnvironment())
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
{
|
{
|
||||||
@@ -133,7 +135,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
await testEnvironment.StartServer();
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
var options = new MqttClientOptionsBuilder().WithClientId("1").WithKeepAlivePeriod(TimeSpan.FromSeconds(5));
|
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(1)).WithProtocolVersion(protocolVersion);
|
||||||
|
|
||||||
var hasReceive = false;
|
var hasReceive = false;
|
||||||
|
|
||||||
@@ -147,41 +149,26 @@ namespace MQTTnet.Tests.Server
|
|||||||
|
|
||||||
// Try to connect 50 clients at the same time.
|
// 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 clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(i => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
|
||||||
var connectedClients = clients.Where(c => c?.IsConnected ?? false).ToList();
|
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(10));
|
||||||
|
|
||||||
|
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
Assert.AreEqual(1, connectedClients.Count);
|
Assert.AreEqual(1, connectedClients.Count);
|
||||||
|
|
||||||
await Task.Delay(5000);
|
|
||||||
|
|
||||||
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
|
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
|
||||||
var sendClient = await testEnvironment.ConnectClient(option2);
|
var sendClient = await testEnvironment.ConnectClient(option2);
|
||||||
await sendClient.PublishStringAsync("aaa", "1");
|
await sendClient.PublishStringAsync("aaa", "1");
|
||||||
|
|
||||||
await Task.Delay(3000);
|
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
Assert.AreEqual(true, hasReceive);
|
Assert.AreEqual(true, hasReceive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
[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]
|
[DataTestMethod]
|
||||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||||
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
|
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
|
||||||
@@ -201,7 +188,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c.AutoAcknowledge = false;
|
c.AutoAcknowledge = false;
|
||||||
++count;
|
++count;
|
||||||
Console.WriteLine("process");
|
Console.WriteLine("process");
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await subscriber.SubscribeAsync("#", qos);
|
await subscriber.SubscribeAsync("#", qos);
|
||||||
@@ -220,6 +207,37 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Session_Takeover()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var options = new MqttClientOptionsBuilder().WithCleanSession(false)
|
||||||
|
.WithProtocolVersion(MqttProtocolVersion.V500) // Disconnect reason is only available in MQTT 5+
|
||||||
|
.WithClientId("a");
|
||||||
|
|
||||||
|
var client1 = await testEnvironment.ConnectClient(options);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
var disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
|
||||||
|
client1.DisconnectedAsync += c =>
|
||||||
|
{
|
||||||
|
disconnectReason = c.Reason;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client2 = await testEnvironment.ConnectClient(options);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
Assert.IsFalse(client1.IsConnected);
|
||||||
|
Assert.IsTrue(client2.IsConnected);
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttClientDisconnectReason.SessionTakenOver, disconnectReason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Set_Session_Item()
|
public async Task Set_Session_Item()
|
||||||
{
|
{
|
||||||
@@ -233,7 +251,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.SessionItems["can_subscribe_x"] = true;
|
e.SessionItems["can_subscribe_x"] = true;
|
||||||
e.SessionItems["default_payload"] = "Hello World";
|
e.SessionItems["default_payload"] = "Hello World";
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
server.InterceptingSubscriptionAsync += e =>
|
server.InterceptingSubscriptionAsync += e =>
|
||||||
@@ -246,13 +264,13 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
server.InterceptingPublishAsync += e =>
|
server.InterceptingPublishAsync += e =>
|
||||||
{
|
{
|
||||||
e.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(e.SessionItems["default_payload"] as string ?? string.Empty);
|
e.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(e.SessionItems["default_payload"] as string ?? string.Empty);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
string receivedPayload = null;
|
string receivedPayload = null;
|
||||||
@@ -261,7 +279,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
client.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var subscribeResult = await client.SubscribeAsync("x");
|
var subscribeResult = await client.SubscribeAsync("x");
|
||||||
@@ -277,41 +295,82 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<IMqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
|
[TestMethod]
|
||||||
|
public async Task Use_Clean_Session()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
var client = testEnvironment.CreateClient();
|
||||||
|
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).WithCleanSession().Build());
|
||||||
|
|
||||||
|
// Create the session including the subscription.
|
||||||
|
var client1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("a").WithCleanSession(false));
|
||||||
|
await client1.SubscribeAsync("x");
|
||||||
|
await client1.DisconnectAsync();
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
Assert.IsFalse(connectResult.IsSessionPresent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Will_Message_Do_Not_Send_On_Takeover()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var receivedMessagesCount = 0;
|
||||||
|
|
||||||
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
// C1 will receive the last will!
|
||||||
|
var c1 = await testEnvironment.ConnectClient();
|
||||||
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||||
|
|
||||||
|
// C2 has the last will defined.
|
||||||
|
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithClientId("WillOwner");
|
||||||
|
|
||||||
|
await testEnvironment.ConnectClient(clientOptions);
|
||||||
|
|
||||||
|
// C3 will do the connection takeover.
|
||||||
|
await testEnvironment.ConnectClient(clientOptions);
|
||||||
|
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
|
Assert.AreEqual(0, receivedMessagesCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task<IMqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sendClient = await testEnvironment.ConnectClient(options);
|
var client = await testEnvironment.ConnectClient(options).ConfigureAwait(false);
|
||||||
|
|
||||||
sendClient.ApplicationMessageReceivedAsync += e =>
|
client.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
onReceive();
|
onReceive();
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
using (var subscribeTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||||
{
|
{
|
||||||
await sendClient.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, subscribeTimeout.Token).ConfigureAwait(false);
|
await client.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, timeout.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendClient;
|
return client;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<IMqttClient> TryConnect(TestEnvironment testEnvironment, MqttClientOptionsBuilder options)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await testEnvironment.ConnectClient(options);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,9 +9,10 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
{
|
{
|
||||||
@@ -32,7 +33,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
e.Response.ReasonCode = MqttSubscribeReasonCode.TopicFilterInvalid;
|
e.Response.ReasonCode = MqttSubscribeReasonCode.TopicFilterInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = await testEnvironment.ConnectClient();
|
var client = await testEnvironment.ConnectClient();
|
||||||
@@ -45,6 +46,46 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||||
|
public async Task Disconnect_While_Subscribing()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
// The client will be disconnect directly after subscribing!
|
||||||
|
server.ClientSubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
await client.SubscribeAsync("#");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Enqueue_Message_After_Subscription()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
server.ClientSubscribedTopicAsync += e =>
|
||||||
|
{
|
||||||
|
server.InjectApplicationMessage(new InjectedMqttApplicationMessage(new MqttApplicationMessageBuilder().WithTopic("test_topic").Build()));
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(client);
|
||||||
|
|
||||||
|
await client.SubscribeAsync("test_topic");
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
Assert.AreEqual(1, receivedMessages.ReceivedEventArgs.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Intercept_Subscription()
|
public async Task Intercept_Subscription()
|
||||||
{
|
{
|
||||||
@@ -56,7 +97,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
{
|
{
|
||||||
// Set the topic to "a" regards what the client wants to subscribe.
|
// Set the topic to "a" regards what the client wants to subscribe.
|
||||||
e.TopicFilter.Topic = "a";
|
e.TopicFilter.Topic = "a";
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var topicAReceived = false;
|
var topicAReceived = false;
|
||||||
@@ -74,7 +115,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
topicBReceived = true;
|
topicBReceived = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await client.SubscribeAsync("b");
|
await client.SubscribeAsync("b");
|
||||||
@@ -101,7 +142,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
.WithTopicFilter("c", MqttQualityOfServiceLevel.ExactlyOnce)
|
.WithTopicFilter("c", MqttQualityOfServiceLevel.ExactlyOnce)
|
||||||
.WithTopicFilter("d")
|
.WithTopicFilter("d")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var response = await client.SubscribeAsync(subscribeOptions);
|
var response = await client.SubscribeAsync(subscribeOptions);
|
||||||
|
|
||||||
Assert.AreEqual(subscribeOptions.TopicFilters.Count, response.Items.Count);
|
Assert.AreEqual(subscribeOptions.TopicFilters.Count, response.Items.Count);
|
||||||
@@ -121,7 +162,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var i = 0; i < 500; i++)
|
for (var i = 0; i < 500; i++)
|
||||||
@@ -164,7 +205,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
|
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
|
||||||
@@ -204,7 +245,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").Build());
|
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").Build());
|
||||||
@@ -242,7 +283,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").WithTopicFilter("b").WithTopicFilter("c").Build());
|
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").WithTopicFilter("b").WithTopicFilter("c").Build());
|
||||||
@@ -276,7 +317,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
c1.ApplicationMessageReceivedAsync += e =>
|
c1.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref receivedMessagesCount);
|
Interlocked.Increment(ref receivedMessagesCount);
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c2"));
|
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c2"));
|
||||||
@@ -291,7 +332,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientSubscribedTopicAsync += e =>
|
server.ClientSubscribedTopicAsync += e =>
|
||||||
{
|
{
|
||||||
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == c1.Options.ClientId;
|
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == c1.Options.ClientId;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
await c1.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
||||||
@@ -306,7 +347,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
server.ClientUnsubscribedTopicAsync += e =>
|
server.ClientUnsubscribedTopicAsync += e =>
|
||||||
{
|
{
|
||||||
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == c1.Options.ClientId;
|
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == c1.Options.ClientId;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.UnsubscribeAsync("a");
|
await c1.UnsubscribeAsync("a");
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Protocol;
|
using MQTTnet.Protocol;
|
||||||
using MQTTnet.Server;
|
using MQTTnet.Server;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
@@ -13,49 +13,301 @@ using MQTTnet.Tests.Mockups;
|
|||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class Subscription_TopicHash_Tests
|
public sealed class SubscriptionTopicHashTests
|
||||||
{
|
{
|
||||||
enum TopicHashSelector { NoWildcard, SingleWildcard, MultiWildcard };
|
MqttSession _clientSession;
|
||||||
|
|
||||||
|
/// <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()
|
||||||
|
{
|
||||||
|
const int numPublishers = 5000;
|
||||||
|
const int numTopicsPerPublisher = 10;
|
||||||
|
|
||||||
|
TopicGenerator.Generate(
|
||||||
|
numPublishers,
|
||||||
|
numTopicsPerPublisher,
|
||||||
|
out var topicsByPublisher,
|
||||||
|
out _,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
// There will be many 'similar' topics ending with, i.e. "sensor100", "sensor101", ...
|
||||||
|
// Hash bucket depths should remain low.
|
||||||
|
var bucketDepths = new Dictionary<ulong, int>();
|
||||||
|
var maxBucketDepth = 0;
|
||||||
|
ulong maxBucketDepthHash = 0;
|
||||||
|
|
||||||
|
var topicsByHash = new Dictionary<ulong, List<string>>();
|
||||||
|
|
||||||
|
foreach (var t in topicsByPublisher)
|
||||||
|
{
|
||||||
|
var topics = t.Value;
|
||||||
|
foreach (var topic in topics)
|
||||||
|
{
|
||||||
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var hashMask, out var 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");
|
||||||
|
}
|
||||||
|
|
||||||
MQTTnet.Server.MqttSession _clientSession;
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Match_Hash_Test_SingleWildCard()
|
public void Check_Selected_Topic_Hashes()
|
||||||
{
|
{
|
||||||
var l0 = "pub0";
|
CheckTopicHash("client1/building1/level1/sensor1", 0x655D4AF100000000, 0xFFFFFFFFFFFFFFFF);
|
||||||
var l1 = "topic1";
|
CheckTopicHash("client1/building1/+/sensor1", 0x655D00F100000000, 0xFFFF00FFFFFFFFFF);
|
||||||
var l2 = "+";
|
CheckTopicHash("client1/+/level1/+", 0x65004A0000000000, 0xFF00FF00FFFFFFFF);
|
||||||
var l3 = "prop1";
|
CheckTopicHash("client1/building1/level1/#", 0x655D4A0000000000, 0xFFFFFF0000000000);
|
||||||
var topic = string.Format("{0}/{1}/{2}/{3}", l0, l1, l2, l3);
|
CheckTopicHash("client1/+/level1/#", 0x65004A0000000000, 0xFF00FF0000000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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/#";
|
||||||
|
|
||||||
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
UInt64 topicHash;
|
/// <summary>
|
||||||
UInt64 topicHashMask;
|
/// Test long topic name with last level being +
|
||||||
bool topicHasWildcard;
|
/// </summary>
|
||||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
[TestMethod]
|
||||||
|
public void Match_Hash_Test_LongTopic_DetectSingleLevelWildcard()
|
||||||
|
{
|
||||||
|
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/+";
|
||||||
|
|
||||||
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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
|
||||||
|
|
||||||
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
|
|
||||||
var hashBytes = GetBytes(topicHash);
|
var hashBytes = GetBytes(topicHash);
|
||||||
Assert.AreNotEqual(hashBytes[0], 0, "checksum 0 mismatch");
|
// all bytes should contain checksum
|
||||||
Assert.AreNotEqual(hashBytes[1], 0, "checksum 1 mismatch");
|
var count = 0;
|
||||||
Assert.AreEqual(hashBytes[2], 0, "checksum 2 mismatch");
|
foreach (var h in hashBytes)
|
||||||
Assert.AreNotEqual(hashBytes[3], 0, "checksum 3 mismatch");
|
{
|
||||||
Assert.AreEqual(hashBytes[4], 0, "checksum 4 mismatch");
|
if (count < 7)
|
||||||
Assert.AreEqual(hashBytes[5], 0, "checksum 5 mismatch");
|
{
|
||||||
Assert.AreEqual(hashBytes[6], 0, "checksum 6 mismatch");
|
Assert.AreNotEqual(h, 0, "checksum mismatch");
|
||||||
Assert.AreEqual(hashBytes[7], 0, "checksum 7 mismatch");
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.AreEqual(h, 0, "checksum mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
// The mask should have zeroes where the wildcard and ff at the end
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The mask should have ff except for last level
|
||||||
var hashMaskBytes = GetBytes(topicHashMask);
|
var hashMaskBytes = GetBytes(topicHashMask);
|
||||||
Assert.AreEqual(hashMaskBytes[0], 0xff, "mask 0 mismatch");
|
count = 0;
|
||||||
Assert.AreEqual(hashMaskBytes[1], 0xff, "mask 1 mismatch");
|
foreach (var h in hashMaskBytes)
|
||||||
Assert.AreEqual(hashMaskBytes[2], 0, "mask 2 mismatch");
|
{
|
||||||
Assert.AreEqual(hashMaskBytes[3], 0xff, "mask 3 mismatch");
|
if (count < 7)
|
||||||
Assert.AreEqual(hashMaskBytes[4], 0xff, "mask 4 mismatch");
|
{
|
||||||
Assert.AreEqual(hashMaskBytes[5], 0xff, "mask 5 mismatch");
|
Assert.AreEqual(h, 0xff, "mask mismatch");
|
||||||
Assert.AreEqual(hashMaskBytes[6], 0xff, "mask 6 mismatch");
|
}
|
||||||
Assert.AreEqual(hashMaskBytes[7], 0xff, "mask 7 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();
|
||||||
|
|
||||||
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
|
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not present");
|
||||||
|
|
||||||
|
|
||||||
|
var hashBytes = GetBytes(topicHash);
|
||||||
|
// all bytes should contain checksum
|
||||||
|
var 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_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();
|
||||||
|
|
||||||
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
|
|
||||||
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
|
|
||||||
|
|
||||||
|
var hashBytes = GetBytes(topicHash);
|
||||||
|
// all bytes should contain checksum
|
||||||
|
var 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -65,13 +317,9 @@ namespace MQTTnet.Tests.Server
|
|||||||
var l1 = "topic1";
|
var l1 = "topic1";
|
||||||
var l2 = "sub1";
|
var l2 = "sub1";
|
||||||
var l3 = "#";
|
var l3 = "#";
|
||||||
var topic = string.Format("{0}/{1}/{2}/{3}", l0, l1, l2, l3);
|
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||||
|
|
||||||
UInt64 topicHash;
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
UInt64 topicHashMask;
|
|
||||||
bool topicHasWildcard;
|
|
||||||
|
|
||||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
|
||||||
|
|
||||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
|
|
||||||
@@ -104,13 +352,9 @@ namespace MQTTnet.Tests.Server
|
|||||||
var l1 = "topic1";
|
var l1 = "topic1";
|
||||||
var l2 = "sub1";
|
var l2 = "sub1";
|
||||||
var l3 = "prop1";
|
var l3 = "prop1";
|
||||||
var topic = string.Format("{0}/{1}/{2}/{3}", l0, l1, l2, l3);
|
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||||
|
|
||||||
UInt64 topicHash;
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
UInt64 topicHashMask;
|
|
||||||
bool topicHasWildcard;
|
|
||||||
|
|
||||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
|
||||||
|
|
||||||
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not wildcard present");
|
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not wildcard present");
|
||||||
|
|
||||||
@@ -137,225 +381,13 @@ namespace MQTTnet.Tests.Server
|
|||||||
Assert.AreEqual(hashMaskBytes[7], 0xff, "mask 7 mismatch");
|
Assert.AreEqual(hashMaskBytes[7], 0xff, "mask 7 mismatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test long topic name with last level being #
|
|
||||||
/// </summary>
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Match_Hash_Test_LongTopic_MultiWildcard()
|
public async Task Match_Hash_Test_Search_MultiWildcard()
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var topics = await PrepareTopicHashSubscriptions(TopicHashSelector.MultiWildcard);
|
||||||
const int NumLevels = 8;
|
var matchCount = CheckTopicSubscriptions(_clientSession, topics, "Multi Wildcard");
|
||||||
var levelNames = new string[NumLevels];
|
// Should match all topics
|
||||||
for (var i = 0; i < NumLevels; ++i)
|
Assert.AreEqual(topics.Count, matchCount, "Topics not matched");
|
||||||
{
|
|
||||||
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]
|
[TestMethod]
|
||||||
@@ -407,134 +439,125 @@ namespace MQTTnet.Tests.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Match_Hash_Test_Search_MultiWildcard()
|
public void Match_Hash_Test_SingleWildCard()
|
||||||
{
|
{
|
||||||
var topics = await PrepareTopicHashSubscriptions(TopicHashSelector.MultiWildcard);
|
var l0 = "pub0";
|
||||||
var matchCount = CheckTopicSubscriptions(_clientSession, topics, "Multi Wildcard");
|
var l1 = "topic1";
|
||||||
// Should match all topics
|
var l2 = "+";
|
||||||
Assert.AreEqual(topics.Count, matchCount, "Topics not matched");
|
var l3 = "prop1";
|
||||||
}
|
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||||
|
|
||||||
/// <summary>
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||||
/// 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;
|
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||||
const int NumTopicsPerPublisher = 10;
|
|
||||||
|
|
||||||
TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
|
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");
|
||||||
|
|
||||||
// There will be many 'similar' topics ending with, i.e. "sensor100", "sensor101", ...
|
// The mask should have zeroes where the wildcard and ff at the end
|
||||||
// Hash bucket depths should remain low.
|
var hashMaskBytes = GetBytes(topicHashMask);
|
||||||
var bucketDepths = new Dictionary<UInt64, int>();
|
Assert.AreEqual(hashMaskBytes[0], 0xff, "mask 0 mismatch");
|
||||||
int maxBucketDepth = 0;
|
Assert.AreEqual(hashMaskBytes[1], 0xff, "mask 1 mismatch");
|
||||||
UInt64 maxBucketDepthHash = 0;
|
Assert.AreEqual(hashMaskBytes[2], 0, "mask 2 mismatch");
|
||||||
|
Assert.AreEqual(hashMaskBytes[3], 0xff, "mask 3 mismatch");
|
||||||
var topicsByHash = new Dictionary<UInt64, List<string>>();
|
Assert.AreEqual(hashMaskBytes[4], 0xff, "mask 4 mismatch");
|
||||||
|
Assert.AreEqual(hashMaskBytes[5], 0xff, "mask 5 mismatch");
|
||||||
foreach (var t in topicsByPublisher)
|
Assert.AreEqual(hashMaskBytes[6], 0xff, "mask 6 mismatch");
|
||||||
{
|
Assert.AreEqual(hashMaskBytes[7], 0xff, "mask 7 mismatch");
|
||||||
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)
|
void CheckTopicHash(string topic, ulong expectedHash, ulong expectedHashMask)
|
||||||
{
|
{
|
||||||
ulong topicHash;
|
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
||||||
ulong hashMask;
|
|
||||||
bool hasWildcard;
|
|
||||||
|
|
||||||
MQTTnet.Server.MqttSubscription.CalculateTopicHash(topic, out topicHash, out hashMask, out hasWildcard);
|
|
||||||
|
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("Topic: " + topic);
|
Console.WriteLine("Topic: " + topic);
|
||||||
Console.WriteLine(string.Format("Hash: {0:X8}", topicHash));
|
Console.WriteLine($"Hash: {topicHash:X8}");
|
||||||
Console.WriteLine(string.Format("Hash Mask: {0:X8}", hashMask));
|
Console.WriteLine($"Hash Mask: {hashMask:X8}");
|
||||||
|
|
||||||
Assert.AreEqual(expectedHash, topicHash, "Topic hash not as expected. Has the hash function changed?");
|
Assert.AreEqual(expectedHash, topicHash, "Topic hash not as expected. Has the hash function changed?");
|
||||||
Assert.AreEqual(expectedHashMask, hashMask, "Topic hash mask not as expected");
|
Assert.AreEqual(expectedHashMask, hashMask, "Topic hash mask not as expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CheckTopicSubscriptions(MqttSession clientSession, List<string> topicsToFind, string subject)
|
||||||
|
{
|
||||||
|
var matchCount = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
var resultCount = 0;
|
||||||
|
|
||||||
|
var stopWatch = new Stopwatch();
|
||||||
|
stopWatch.Start();
|
||||||
|
var countUp = 0;
|
||||||
|
var countDown = topicsToFind.Count - 1;
|
||||||
|
for (; countUp < topicsToFind.Count; ++countUp, --countDown)
|
||||||
|
{
|
||||||
|
var topicToFind = topicsToFind[countDown];
|
||||||
|
|
||||||
|
MqttSubscription.CalculateTopicHash(topicToFind, out var topicHash, out _, out _);
|
||||||
|
|
||||||
|
clientSession.TryCheckSubscriptions(topicToFind, topicHash, MqttQualityOfServiceLevel.AtMostOnce, "OtherClient", out var result);
|
||||||
|
if (result.IsSubscribed)
|
||||||
|
{
|
||||||
|
++matchCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
++resultCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopWatch.Stop();
|
||||||
|
|
||||||
|
Console.Write("Match count: " + matchCount + "; ");
|
||||||
|
|
||||||
|
Console.WriteLine(subject + " lookup milliseconds: " + stopWatch.ElapsedMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] GetBytes(ulong 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async Task<List<string>> PrepareTopicHashSubscriptions(TopicHashSelector selector)
|
async Task<List<string>> PrepareTopicHashSubscriptions(TopicHashSelector selector)
|
||||||
{
|
{
|
||||||
Dictionary<string, List<string>> topicsByPublisher;
|
const int numPublishers = 1;
|
||||||
Dictionary<string, List<string>> singleWildcardTopicsByPublisher;
|
const int numTopicsPerPublisher = 10000;
|
||||||
Dictionary<string, List<string>> multiWildcardTopicsByPublisher;
|
|
||||||
|
|
||||||
const int NumPublishers = 1;
|
TopicGenerator.Generate(
|
||||||
const int NumTopicsPerPublisher = 10000;
|
numPublishers,
|
||||||
|
numTopicsPerPublisher,
|
||||||
TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
|
out var topicsByPublisher,
|
||||||
|
out var singleWildcardTopicsByPublisher,
|
||||||
|
out var multiWildcardTopicsByPublisher);
|
||||||
|
|
||||||
var topics = topicsByPublisher.FirstOrDefault().Value;
|
var topics = topicsByPublisher.FirstOrDefault().Value;
|
||||||
var singleWildcardTopics = singleWildcardTopicsByPublisher.FirstOrDefault().Value;
|
var singleWildcardTopics = singleWildcardTopicsByPublisher.FirstOrDefault().Value;
|
||||||
var multiWildcardTopics = multiWildcardTopicsByPublisher.FirstOrDefault().Value;
|
var multiWildcardTopics = multiWildcardTopicsByPublisher.FirstOrDefault().Value;
|
||||||
|
|
||||||
const string ClientId = "Client1";
|
const string clientId = "Client1";
|
||||||
var logger = new Mockups.TestLogger();
|
var logger = new TestLogger();
|
||||||
var serverOptions = new MQTTnet.Server.MqttServerOptions();
|
var serverOptions = new MqttServerOptions();
|
||||||
var eventContainer = new MQTTnet.Server.MqttServerEventContainer();
|
var eventContainer = new MqttServerEventContainer();
|
||||||
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
|
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
|
||||||
var sessionManager = new MqttClientSessionsManager(serverOptions, retainedMessagesManager, eventContainer, logger);
|
var sessionManager = new MqttClientSessionsManager(serverOptions, retainedMessagesManager, eventContainer, logger);
|
||||||
_clientSession = new MQTTnet.Server.MqttSession(
|
_clientSession = new MqttSession(clientId, false, new Dictionary<object, object>(), serverOptions, eventContainer, retainedMessagesManager, sessionManager);
|
||||||
ClientId,
|
|
||||||
false,
|
|
||||||
new Dictionary<object, object>(),
|
|
||||||
serverOptions,
|
|
||||||
eventContainer,
|
|
||||||
retainedMessagesManager,
|
|
||||||
sessionManager
|
|
||||||
);
|
|
||||||
|
|
||||||
List<string> topicsToSubscribe;
|
List<string> topicsToSubscribe;
|
||||||
|
|
||||||
@@ -550,63 +573,26 @@ namespace MQTTnet.Tests.Server
|
|||||||
topicsToSubscribe = topics;
|
topicsToSubscribe = topics;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var t in topicsToSubscribe)
|
foreach (var t in topicsToSubscribe)
|
||||||
{
|
{
|
||||||
var subPacket = new Packets.MqttSubscribePacket();
|
var subPacket = new MqttSubscribePacket();
|
||||||
var filter = new Packets.MqttTopicFilter();
|
var filter = new MqttTopicFilter
|
||||||
filter.Topic = t;
|
{
|
||||||
|
Topic = t
|
||||||
|
};
|
||||||
subPacket.TopicFilters.Add(filter);
|
subPacket.TopicFilters.Add(filter);
|
||||||
await _clientSession.SubscriptionsManager.Subscribe(subPacket, default(CancellationToken));
|
await _clientSession.Subscribe(subPacket, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
return topics;
|
return topics;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CheckTopicSubscriptions(MQTTnet.Server.MqttSession clientSession, List<string> topicsToFind, string subject)
|
enum TopicHashSelector
|
||||||
{
|
{
|
||||||
var matchCount = 0;
|
NoWildcard,
|
||||||
|
SingleWildcard,
|
||||||
{
|
MultiWildcard
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Internal;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server
|
namespace MQTTnet.Tests.Server
|
||||||
{
|
{
|
||||||
@@ -50,7 +50,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
receivedTopics.Add(e.ApplicationMessage.Topic);
|
receivedTopics.Add(e.ApplicationMessage.Topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await c1.SubscribeAsync("#");
|
await c1.SubscribeAsync("#");
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// 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.Protocol;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests.Server
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class Unsubscribe_Tests : BaseTestClass
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||||
|
public async Task Disconnect_While_Unsubscribing()
|
||||||
|
{
|
||||||
|
using (var testEnvironment = CreateTestEnvironment())
|
||||||
|
{
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
// The client will be disconnect directly after subscribing!
|
||||||
|
server.ClientUnsubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
|
||||||
|
|
||||||
|
var client = await testEnvironment.ConnectClient();
|
||||||
|
await client.SubscribeAsync("#");
|
||||||
|
await client.UnsubscribeAsync("#");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|||||||
using MQTTnet.Client;
|
using MQTTnet.Client;
|
||||||
using MQTTnet.Formatter;
|
using MQTTnet.Formatter;
|
||||||
using MQTTnet.Implementations;
|
using MQTTnet.Implementations;
|
||||||
|
using MQTTnet.Internal;
|
||||||
using MQTTnet.Packets;
|
using MQTTnet.Packets;
|
||||||
using MQTTnet.Tests.Mockups;
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ namespace MQTTnet.Tests.Server
|
|||||||
receiver.ApplicationMessageReceivedAsync += e =>
|
receiver.ApplicationMessageReceivedAsync += e =>
|
||||||
{
|
{
|
||||||
receivedMessage = e.ApplicationMessage;
|
receivedMessage = e.ApplicationMessage;
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user