mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 17:00:03 +08:00
Compare commits
19
Commits
v4.0.1.184
...
v4.0.2.221
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 -->
|
||||
- Client
|
||||
- ManagedClient
|
||||
- MQTTnet.Server standalone
|
||||
- Server
|
||||
- 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 -->
|
||||
- Client
|
||||
- ManagedClient
|
||||
- MQTTnet.Server standalone
|
||||
- Server
|
||||
- Generic
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
* [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).
|
||||
* [Core] Updated nuget packages.
|
||||
* [Core] The option _IgnoreCertificateChainErrors_ is now respected (thanks to @GodVenn, #1447).
|
||||
* [ManagedClient] The managed client now sends the entire topic filter including new MQTTv5 properties when subscribing.
|
||||
* [ManagedClient] Fixed wrong firing of _ApplicationMessageSkippedAsync_ event (thanks to @quackgizmo, #1460).
|
||||
* [Server] Fixed reporting of _MaximumQoS_ in _ConnAck_ packet (MQTTv5 only) (#1442).
|
||||
* [Server] Fix cross thread issue in session message storage for QoS 1 and 2.
|
||||
* [Server] The event _ClientSubscribedTopicAsync_ is now fired after the subscription is completely processed internally (#1435).
|
||||
* [Server] Improved CPU usage on lower end machines (#788).
|
||||
@@ -3,7 +3,7 @@ name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
VERSION: "4.0.1.${{github.run_number}}"
|
||||
VERSION: "4.0.2.${{github.run_number}}"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -111,13 +111,21 @@ public static class Client_Connection_Samples
|
||||
.WithTls(
|
||||
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;
|
||||
})
|
||||
.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.");
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,19 +167,59 @@ public static class Client_Connection_Samples
|
||||
.WithTls(
|
||||
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();
|
||||
|
||||
// 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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Ping_Server()
|
||||
{
|
||||
/*
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.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.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj" />
|
||||
<ProjectReference Include="..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj" />
|
||||
|
||||
+9
-3
@@ -39,10 +39,16 @@ Console.WriteLine();
|
||||
|
||||
try
|
||||
{
|
||||
var task = selectedSampleMethod.Invoke(null, null) as Task;
|
||||
task?.Wait();
|
||||
if (selectedSampleMethod.Invoke(null, null) is Task task)
|
||||
{
|
||||
await task;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Press Enter to exit.");
|
||||
Console.ReadLine();
|
||||
@@ -0,0 +1,74 @@
|
||||
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,7 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
@@ -9,27 +10,95 @@ namespace MQTTnet.Samples.Server;
|
||||
|
||||
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()
|
||||
{
|
||||
/*
|
||||
* This sample starts a simple MQTT server which will accept any TCP connection.
|
||||
*/
|
||||
|
||||
|
||||
var mqttFactory = new MqttFactory();
|
||||
|
||||
// The port for the default endpoint is 1883.
|
||||
// The default endpoint is NOT encrypted!
|
||||
// Use the builder classes where possible.
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder()
|
||||
.WithDefaultEndpoint()
|
||||
.Build();
|
||||
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
|
||||
// The port can be changed using the following API (not used in this example).
|
||||
new MqttServerOptionsBuilder()
|
||||
.WithDefaultEndpoint()
|
||||
.WithDefaultEndpointPort(1234)
|
||||
.Build();
|
||||
|
||||
// new MqttServerOptionsBuilder()
|
||||
// .WithDefaultEndpoint()
|
||||
// .WithDefaultEndpointPort(1234)
|
||||
// .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))
|
||||
{
|
||||
await mqttServer.StartAsync();
|
||||
@@ -52,9 +121,7 @@ public static class Server_Simple_Samples
|
||||
|
||||
var mqttFactory = new MqttFactory();
|
||||
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder()
|
||||
.WithDefaultEndpoint()
|
||||
.Build();
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
|
||||
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
||||
{
|
||||
@@ -88,4 +155,61 @@ public static class Server_Simple_Samples
|
||||
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>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</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>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace MQTTnet.Benchmarks
|
||||
|
||||
_stream.Position = 0;
|
||||
|
||||
var channel = new TestMqttChannel(_stream);
|
||||
var channel = new MemoryMqttChannel(_stream);
|
||||
|
||||
_channelAdapter = new MqttChannelAdapter(channel, serializer, null, new MqttNetEventLogger());
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[RPlotExporter]
|
||||
[MemoryDiagnoser]
|
||||
public class LoggerBenchmark
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -6,11 +6,10 @@ using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Server;
|
||||
using MqttClient = MQTTnet.Client.MqttClient;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[RPlotExporter, RankColumn]
|
||||
[MemoryDiagnoser]
|
||||
public class MessageProcessingBenchmark
|
||||
|
||||
@@ -13,7 +13,7 @@ using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public class MessageProcessingMqttConnectionContextBenchmark
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public class MqttPacketReaderWriterBenchmark
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public sealed class MqttTcpChannelBenchmark
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using MQTTnet.Tests.Server;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[RPlotExporter, RankColumn]
|
||||
[MemoryDiagnoser]
|
||||
public class RoundtripProcessingBenchmark
|
||||
|
||||
@@ -17,7 +17,7 @@ using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[RPlotExporter]
|
||||
[MemoryDiagnoser]
|
||||
public class SerializerBenchmark : BaseBenchmark
|
||||
|
||||
@@ -5,7 +5,7 @@ using MQTTnet.Tests.Server;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[RPlotExporter, RankColumn]
|
||||
[MemoryDiagnoser]
|
||||
public class ServerProcessingBenchmark
|
||||
|
||||
@@ -13,7 +13,7 @@ using MQTTnet.AspNetCore.Client.Tcp;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public class TcpPipesBenchmark
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ using BenchmarkDotNet.Jobs;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[RPlotExporter]
|
||||
[MemoryDiagnoser]
|
||||
public class TopicFilterComparerBenchmark
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
</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>
|
||||
|
||||
@@ -51,9 +51,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
/// cref="MaintainConnectionAsync" />
|
||||
/// .
|
||||
/// </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 HashSet<string> _unsubscriptions = new HashSet<string>();
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_applicationMessageSkippedEvent.HasHandlers)
|
||||
if (applicationMessageSkippedEventArgs != null && _applicationMessageSkippedEvent.HasHandlers)
|
||||
{
|
||||
await _applicationMessageSkippedEvent.InvokeAsync(applicationMessageSkippedEventArgs).ConfigureAwait(false);
|
||||
}
|
||||
@@ -289,7 +289,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
foreach (var topicFilter in topicFilters)
|
||||
{
|
||||
_subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
|
||||
_subscriptions[topicFilter.Topic] = topicFilter;
|
||||
_unsubscriptions.Remove(topicFilter.Topic);
|
||||
}
|
||||
}
|
||||
@@ -455,18 +455,11 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
if (_reconnectSubscriptions.Any())
|
||||
{
|
||||
var subscriptions = _reconnectSubscriptions.Select(
|
||||
i => new MqttTopicFilter
|
||||
{
|
||||
Topic = i.Key,
|
||||
QualityOfServiceLevel = i.Value
|
||||
});
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -495,14 +488,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
lock (_subscriptions)
|
||||
{
|
||||
subscriptions = _subscriptions.Select(
|
||||
i => new MqttTopicFilter
|
||||
{
|
||||
Topic = i.Key,
|
||||
QualityOfServiceLevel = i.Value
|
||||
})
|
||||
.ToList();
|
||||
|
||||
subscriptions = _subscriptions.Values.ToList();
|
||||
_subscriptions.Clear();
|
||||
|
||||
unsubscriptions = new HashSet<string>(_unsubscriptions);
|
||||
@@ -523,7 +509,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
foreach (var subscription in subscriptions)
|
||||
{
|
||||
_reconnectSubscriptions[subscription.Topic] = subscription.QualityOfServiceLevel;
|
||||
_reconnectSubscriptions[subscription.Topic] = subscription;
|
||||
}
|
||||
|
||||
var addedTopicFilters = new List<MqttTopicFilter>();
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
</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>
|
||||
|
||||
@@ -57,15 +57,15 @@
|
||||
</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>
|
||||
<PackageReference Include="WebSocket4Net" Version="0.15.2"/>
|
||||
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj"/>
|
||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<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.Net.Compilers" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
@@ -23,10 +23,4 @@
|
||||
<ProjectReference Include="..\..\Source\MQTTnet\MQTTnet.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Start.bat">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
dotnet MQTTnet.TestApp.NetCore.dll
|
||||
@@ -563,7 +563,7 @@ namespace MQTTnet.Tests
|
||||
|
||||
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(
|
||||
channel,
|
||||
@@ -583,7 +583,7 @@ namespace MQTTnet.Tests
|
||||
|
||||
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());
|
||||
|
||||
adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||
@@ -603,7 +603,7 @@ namespace MQTTnet.Tests
|
||||
var serializer = MqttPacketFormatterAdapter.GetMqttPacketFormatter(protocolVersion, writer);
|
||||
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());
|
||||
return (TPacket)adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.Net.Compilers" Version="4.0.1" />
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace MQTTnet.Tests.MQTTv5
|
||||
|
||||
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");
|
||||
|
||||
|
||||
+3
-3
@@ -10,16 +10,16 @@ using MQTTnet.Channel;
|
||||
|
||||
namespace MQTTnet.Tests.Mockups
|
||||
{
|
||||
public sealed class TestMqttChannel : IMqttChannel
|
||||
public sealed class MemoryMqttChannel : IMqttChannel
|
||||
{
|
||||
readonly MemoryStream _stream;
|
||||
|
||||
public TestMqttChannel(MemoryStream stream)
|
||||
public MemoryMqttChannel(MemoryStream stream)
|
||||
{
|
||||
_stream = stream;
|
||||
}
|
||||
|
||||
public TestMqttChannel(byte[] buffer)
|
||||
public MemoryMqttChannel(byte[] buffer)
|
||||
{
|
||||
_stream = new MemoryStream(buffer);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace MQTTnet.Tests
|
||||
|
||||
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));
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client;
|
||||
@@ -15,6 +18,104 @@ namespace MQTTnet.Tests.Server
|
||||
[TestClass]
|
||||
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 PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ValidatingConnectionAsync += eventArgs =>
|
||||
{
|
||||
if (eventArgs.Username == "SECRET")
|
||||
{
|
||||
eventArgs.ReasonCode = MqttConnectReasonCode.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
eventArgs.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
||||
}
|
||||
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
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]
|
||||
public async Task Use_Username_Null_Password_Empty()
|
||||
{
|
||||
@@ -29,10 +130,7 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithCredentials(username, password)
|
||||
.Build();
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(username, password).Build();
|
||||
|
||||
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions));
|
||||
Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException));
|
||||
@@ -41,25 +139,7 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_UserName()
|
||||
{
|
||||
return TestCredentials("x", "Password1");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_Password()
|
||||
{
|
||||
return TestCredentials("UserName", "x");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_UserName_And_Password()
|
||||
{
|
||||
return TestCredentials("x", "x");
|
||||
}
|
||||
|
||||
private async Task TestCredentials(string userName, string password)
|
||||
async Task TestCredentials(string userName, string password)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
@@ -84,10 +164,7 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithCredentials(userName, password)
|
||||
.Build();
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(userName, password).Build();
|
||||
|
||||
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions));
|
||||
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, ex.Result.ResultCode);
|
||||
|
||||
@@ -12,6 +12,7 @@ using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
@@ -45,6 +46,30 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
}
|
||||
|
||||
[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 PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(client);
|
||||
|
||||
await client.SubscribeAsync("test_topic");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, receivedMessages.ReceivedEventArgs.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Subscription()
|
||||
{
|
||||
@@ -101,7 +126,7 @@ namespace MQTTnet.Tests.Server
|
||||
.WithTopicFilter("c", MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
.WithTopicFilter("d")
|
||||
.Build();
|
||||
|
||||
|
||||
var response = await client.SubscribeAsync(subscribeOptions);
|
||||
|
||||
Assert.AreEqual(subscribeOptions.TopicFilters.Count, response.Items.Count);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
@@ -25,6 +26,7 @@ namespace MQTTnet.Formatter
|
||||
SubscriptionIdentifiersAvailable = true,
|
||||
SharedSubscriptionAvailable = false,
|
||||
TopicAliasMaximum = ushort.MaxValue,
|
||||
MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce,
|
||||
WildcardSubscriptionAvailable = true,
|
||||
|
||||
AuthenticationMethod = validatingConnectionEventArgs.AuthenticationMethod,
|
||||
@@ -32,7 +34,11 @@ namespace MQTTnet.Formatter
|
||||
AssignedClientIdentifier = validatingConnectionEventArgs.AssignedClientIdentifier,
|
||||
ReasonString = validatingConnectionEventArgs.ReasonString,
|
||||
ServerReference = validatingConnectionEventArgs.ServerReference,
|
||||
UserProperties = validatingConnectionEventArgs.ResponseUserProperties
|
||||
UserProperties = validatingConnectionEventArgs.ResponseUserProperties,
|
||||
|
||||
ResponseInformation = null,
|
||||
MaximumPacketSize = 0, // Unlimited,
|
||||
ReceiveMaximum = 0 // Unlimited
|
||||
};
|
||||
|
||||
return connAckPacket;
|
||||
|
||||
@@ -72,11 +72,11 @@ namespace MQTTnet.Formatter.V5
|
||||
|
||||
if (value == MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
{
|
||||
Write(MqttPropertyId.MaximumQoS, true);
|
||||
Write(MqttPropertyId.MaximumQoS, 0x1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Write(MqttPropertyId.MaximumQoS, false);
|
||||
Write(MqttPropertyId.MaximumQoS, 0x0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#if !WINDOWS_UWP
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Security;
|
||||
@@ -11,9 +14,6 @@ using System.Runtime.ExceptionServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
@@ -77,7 +77,7 @@ namespace MQTTnet.Implementations
|
||||
{
|
||||
socket.LingerState = _tcpOptions.LingerState;
|
||||
}
|
||||
|
||||
|
||||
if (_tcpOptions.DualMode.HasValue)
|
||||
{
|
||||
// It is important to avoid setting the flag if no specific value is set by the user
|
||||
@@ -103,15 +103,20 @@ namespace MQTTnet.Implementations
|
||||
ApplicationProtocols = _tcpOptions.TlsOptions.ApplicationProtocols,
|
||||
ClientCertificates = LoadCertificates(),
|
||||
EnabledSslProtocols = _tcpOptions.TlsOptions.SslProtocol,
|
||||
CertificateRevocationCheckMode = _tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : _tcpOptions.TlsOptions.RevocationMode,
|
||||
CertificateRevocationCheckMode =
|
||||
_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : _tcpOptions.TlsOptions.RevocationMode,
|
||||
TargetHost = _tcpOptions.Server,
|
||||
CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy
|
||||
};
|
||||
|
||||
await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
await sslStream.AuthenticateAsClientAsync(_tcpOptions.Server, LoadCertificates(), _tcpOptions.TlsOptions.SslProtocol,
|
||||
!_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
||||
await sslStream.AuthenticateAsClientAsync(
|
||||
_tcpOptions.Server,
|
||||
LoadCertificates(),
|
||||
_tcpOptions.TlsOptions.SslProtocol,
|
||||
!_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors)
|
||||
.ConfigureAwait(false);
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
@@ -269,6 +274,11 @@ namespace MQTTnet.Implementations
|
||||
return certificateValidationHandler(eventArgs);
|
||||
}
|
||||
|
||||
if (_tcpOptions?.TlsOptions?.IgnoreCertificateChainErrors ?? false)
|
||||
{
|
||||
sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors;
|
||||
}
|
||||
|
||||
return sslPolicyErrors == SslPolicyErrors.None;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</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 Condition="'$(TargetFramework)'=='netstandard1.3'">
|
||||
|
||||
@@ -188,6 +188,8 @@ namespace MQTTnet.Server
|
||||
// own exception in the reading loop!
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
var packet = await ChannelAdapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (packet == null)
|
||||
{
|
||||
@@ -315,6 +317,10 @@ namespace MQTTnet.Server
|
||||
{
|
||||
packetBusItem.MarkAsFailed(exception);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
||||
@@ -175,13 +175,14 @@ namespace MQTTnet.Server
|
||||
};
|
||||
|
||||
var addedSubscriptions = new List<string>();
|
||||
var finalTopicFilters = new List<MqttTopicFilter>();
|
||||
|
||||
// The topic filters are order by its QoS so that the higher QoS will win over a
|
||||
// lower one.
|
||||
foreach (var originalTopicFilter in subscribePacket.TopicFilters.OrderByDescending(f => f.QualityOfServiceLevel))
|
||||
foreach (var topicFilterItem in subscribePacket.TopicFilters.OrderByDescending(f => f.QualityOfServiceLevel))
|
||||
{
|
||||
var subscriptionEventArgs = await InterceptSubscribe(originalTopicFilter, cancellationToken).ConfigureAwait(false);
|
||||
var finalTopicFilter = subscriptionEventArgs.TopicFilter;
|
||||
var subscriptionEventArgs = await InterceptSubscribe(topicFilterItem, cancellationToken).ConfigureAwait(false);
|
||||
var topicFilter = subscriptionEventArgs.TopicFilter;
|
||||
var processSubscription = subscriptionEventArgs.ProcessSubscription && subscriptionEventArgs.Response.ReasonCode <= MqttSubscribeReasonCode.GrantedQoS2;
|
||||
|
||||
result.UserProperties = subscriptionEventArgs.UserProperties;
|
||||
@@ -195,16 +196,26 @@ namespace MQTTnet.Server
|
||||
result.CloseConnection = true;
|
||||
}
|
||||
|
||||
if (!processSubscription || string.IsNullOrEmpty(finalTopicFilter.Topic))
|
||||
if (!processSubscription || string.IsNullOrEmpty(topicFilter.Topic))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var createSubscriptionResult = CreateSubscription(finalTopicFilter, subscribePacket.SubscriptionIdentifier, subscriptionEventArgs.Response.ReasonCode);
|
||||
var createSubscriptionResult = CreateSubscription(topicFilter, subscribePacket.SubscriptionIdentifier, subscriptionEventArgs.Response.ReasonCode);
|
||||
|
||||
addedSubscriptions.Add(finalTopicFilter.Topic);
|
||||
addedSubscriptions.Add(topicFilter.Topic);
|
||||
finalTopicFilters.Add(topicFilter);
|
||||
|
||||
if (_eventContainer.ClientSubscribedTopicEvent.HasHandlers)
|
||||
FilterRetainedApplicationMessages(retainedApplicationMessages, createSubscriptionResult, result);
|
||||
}
|
||||
|
||||
// This call will add the new subscription to the internal storage.
|
||||
// So the event _ClientSubscribedTopicEvent_ must be called afterwards.
|
||||
_subscriptionChangedNotification?.OnSubscriptionsAdded(_session, addedSubscriptions);
|
||||
|
||||
if (_eventContainer.ClientSubscribedTopicEvent.HasHandlers)
|
||||
{
|
||||
foreach (var finalTopicFilter in finalTopicFilters)
|
||||
{
|
||||
var eventArgs = new ClientSubscribedTopicEventArgs
|
||||
{
|
||||
@@ -214,12 +225,8 @@ namespace MQTTnet.Server
|
||||
|
||||
await _eventContainer.ClientSubscribedTopicEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
FilterRetainedApplicationMessages(retainedApplicationMessages, createSubscriptionResult, result);
|
||||
}
|
||||
|
||||
_subscriptionChangedNotification?.OnSubscriptionsAdded(_session, addedSubscriptions);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -263,26 +270,30 @@ namespace MQTTnet.Server
|
||||
_subscriptions.Remove(topicFilter);
|
||||
|
||||
// must remove subscription object from topic hash dictionary also
|
||||
|
||||
if (existingSubscription.TopicHasWildcard)
|
||||
if (existingSubscription != null)
|
||||
{
|
||||
if (_wildcardSubscriptionsByTopicHash.TryGetValue(existingSubscription.TopicHash, out var subs))
|
||||
var topicHash = existingSubscription.TopicHash;
|
||||
|
||||
if (existingSubscription.TopicHasWildcard)
|
||||
{
|
||||
subs.Subscriptions.Remove(existingSubscription);
|
||||
if (subs.Subscriptions.Count == 0)
|
||||
if (_wildcardSubscriptionsByTopicHash.TryGetValue(topicHash, out var subscriptions))
|
||||
{
|
||||
_wildcardSubscriptionsByTopicHash.Remove(existingSubscription.TopicHash);
|
||||
subscriptions.Subscriptions.Remove(existingSubscription);
|
||||
if (subscriptions.Subscriptions.Count == 0)
|
||||
{
|
||||
_wildcardSubscriptionsByTopicHash.Remove(topicHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_noWildcardSubscriptionsByTopicHash.TryGetValue(existingSubscription.TopicHash, out var subs))
|
||||
else
|
||||
{
|
||||
subs.Remove(existingSubscription);
|
||||
if (subs.Count == 0)
|
||||
if (_noWildcardSubscriptionsByTopicHash.TryGetValue(topicHash, out var subscriptions))
|
||||
{
|
||||
_noWildcardSubscriptionsByTopicHash.Remove(existingSubscription.TopicHash);
|
||||
subscriptions.Remove(existingSubscription);
|
||||
if (subscriptions.Count == 0)
|
||||
{
|
||||
_noWildcardSubscriptionsByTopicHash.Remove(topicHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,11 +305,7 @@ namespace MQTTnet.Server
|
||||
finally
|
||||
{
|
||||
_subscriptionsLock.Release();
|
||||
|
||||
if (_subscriptionChangedNotification != null)
|
||||
{
|
||||
_subscriptionChangedNotification.OnSubscriptionsRemoved(_session, removedSubscriptions);
|
||||
}
|
||||
_subscriptionChangedNotification?.OnSubscriptionsRemoved(_session, removedSubscriptions);
|
||||
}
|
||||
|
||||
if (_eventContainer.ClientUnsubscribedTopicEvent.HasHandlers)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -23,7 +24,7 @@ namespace MQTTnet.Server
|
||||
|
||||
readonly MqttServerOptions _serverOptions;
|
||||
|
||||
readonly Dictionary<ushort, MqttPublishPacket> _unacknowledgedPublishPackets = new Dictionary<ushort, MqttPublishPacket>();
|
||||
readonly ConcurrentDictionary<ushort, MqttPublishPacket> _unacknowledgedPublishPackets = new ConcurrentDictionary<ushort, MqttPublishPacket>();
|
||||
|
||||
// Bookkeeping to know if this is a subscribing client; lazy initialize later.
|
||||
HashSet<string> _subscribedTopics;
|
||||
@@ -72,7 +73,7 @@ namespace MQTTnet.Server
|
||||
|
||||
public void AcknowledgePublishPacket(ushort packetIdentifier)
|
||||
{
|
||||
_unacknowledgedPublishPackets.Remove(packetIdentifier);
|
||||
_unacknowledgedPublishPackets.TryRemove(packetIdentifier, out _);
|
||||
}
|
||||
|
||||
public void AddSubscribedTopic(string topic)
|
||||
|
||||
Reference in New Issue
Block a user