mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 08:33:01 +08:00
Compare commits
57
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fdee9e0db | ||
|
|
cae7206733 | ||
|
|
e2e7aacc09 | ||
|
|
0d3dd96f2a | ||
|
|
e0660ba887 | ||
|
|
5965ded488 | ||
|
|
fc31c3077c | ||
|
|
e604345e65 | ||
|
|
111a0d69cf | ||
|
|
1a08ec17ba | ||
|
|
c537d290c3 | ||
|
|
2180f59725 | ||
|
|
b1dc8607af | ||
|
|
101898695e | ||
|
|
9295626503 | ||
|
|
18614d4e81 | ||
|
|
560e80455f | ||
|
|
a97e88be29 | ||
|
|
3b5667c882 | ||
|
|
0790e54794 | ||
|
|
94f9fac2bd | ||
|
|
8ad86928f1 | ||
|
|
d8e5936d45 | ||
|
|
caa717544b | ||
|
|
66b77f8d6d | ||
|
|
ab5264c8ca | ||
|
|
db12309bae | ||
|
|
2953e0dcc5 | ||
|
|
1b18ac9fe3 | ||
|
|
2cdad6622a | ||
|
|
7601d11a7a | ||
|
|
0bb4c6ef09 | ||
|
|
449624fd72 | ||
|
|
99f4f46062 | ||
|
|
b08d11c069 | ||
|
|
ac41e838ec | ||
|
|
836ddf3bbc | ||
|
|
0e815c626c | ||
|
|
4b7d035e9e | ||
|
|
cb3c44f48d | ||
|
|
8b3b9d33de | ||
|
|
59d99509c8 | ||
|
|
2933b76237 | ||
|
|
36c6d8974e | ||
|
|
d6cab76728 | ||
|
|
b4f740f338 | ||
|
|
a004de25ac | ||
|
|
d0584413ca | ||
|
|
ba57537108 | ||
|
|
6dcb110737 | ||
|
|
b599a832da | ||
|
|
9cbd5dd778 | ||
|
|
ed31c51237 | ||
|
|
e01707d25e | ||
|
|
73f118fe1c | ||
|
|
0234918f30 | ||
|
|
c90ba7e061 |
@@ -1,8 +1,2 @@
|
||||
* [Core] Added net7.0 builds (#1635, thanks to @YAJeff).
|
||||
* [Client] Added support for passing MQTT v5 options (User properties etc.) for disconnects.
|
||||
* [Client] An internal exception is no longer caught silently when calling _DisconnectAsync_ to indicate that the disconnect is not clean (BREAKING CHANGE).
|
||||
* [Client] MQTTv5 features are now checked and an exception is thrown if they are used when using protocol version 3.1.1 and lower. These checks can be disabled in client options. (BREAKING CHANGE!).
|
||||
* [Server] Exposed MQTT v5 sent properties from the affected client in _ClientDisconnectedAsync_ event.
|
||||
* [Server] Fixed wrong client ID passed to _InterceptingUnsubscriptionEventArgs_ (#1631, thanks to @ghord).
|
||||
* [Server] Exposed socket settings for TCP keep alive in TCP options (#1544).
|
||||
* [Server] Exposed server session items at server level and allow custom session items for injected application messages (#1638).
|
||||
* [Client] Fixed wrong TLS options handling (#1830).
|
||||
* [Client] Fixed NullReferenceExeption when performing a Ping when the client is not connected (#1831).
|
||||
|
||||
@@ -3,7 +3,7 @@ name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
VERSION: "4.2.0.${{github.run_number}}"
|
||||
VERSION: "4.3.1.${{github.run_number}}"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -50,6 +50,22 @@ 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
|
||||
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
||||
|
||||
## Getting Started
|
||||
|
||||
MQTTnet is delivered via <b>NuGet</b> package manager. You can find the packages here: https://www.nuget.org/packages/MQTTnet/
|
||||
|
||||
Use these command in the Package Manager console to install MQTTnet manually:
|
||||
```
|
||||
Install-Package MQTTnet
|
||||
```
|
||||
|
||||
Samples for using MQTTnet are part of this repository. For starters these samples are recommended:
|
||||
|
||||
- [Connect with a broker](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Connection_Samples.cs)
|
||||
- [Subscribing to data](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Subscribe_Samples.cs)
|
||||
- [Publishing data](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Publish_Samples.cs)
|
||||
- [Host own broker](https://github.com/dotnet/MQTTnet/blob/master/Samples/Server/Server_Simple_Samples.cs)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
|
||||
|
||||
@@ -135,15 +135,15 @@ public static class Client_Connection_Samples
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io")
|
||||
.WithTls(
|
||||
.WithTlsOptions(
|
||||
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;
|
||||
o.WithCertificateValidationHandler(_ => true);
|
||||
|
||||
// The default value is determined by the OS. Set manually to force version.
|
||||
o.SslProtocol = SslProtocols.Tls12;
|
||||
o.WithSslProtocols(SslProtocols.Tls12);
|
||||
})
|
||||
.Build();
|
||||
|
||||
@@ -170,7 +170,7 @@ public static class Client_Connection_Samples
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").Build();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.hivemq.com:8000/mqtt")).Build();
|
||||
|
||||
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
@@ -192,7 +192,7 @@ public static class Client_Connection_Samples
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").Build();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.hivemq.com:8000/mqtt")).Build();
|
||||
|
||||
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
@@ -215,13 +215,11 @@ public static class Client_Connection_Samples
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
|
||||
.WithTls(
|
||||
o =>
|
||||
{
|
||||
.WithTlsOptions(
|
||||
o => o.WithCertificateValidationHandler(
|
||||
// The used public broker sometimes has invalid certificates. This sample accepts all
|
||||
// certificates. This should not be used in live environments.
|
||||
o.CertificateValidationHandler = _ => true;
|
||||
})
|
||||
_ => true))
|
||||
.Build();
|
||||
|
||||
// In MQTTv5 the response contains much more information.
|
||||
@@ -236,6 +234,31 @@ public static class Client_Connection_Samples
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Connect_With_Amazon_AWS()
|
||||
{
|
||||
/*
|
||||
* This sample creates a simple MQTT client and connects to an Amazon Web Services broker.
|
||||
*
|
||||
* The broker requires special settings which are set here.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("amazon.web.services.broker")
|
||||
// Disabling packet fragmentation is very important!
|
||||
.WithoutPacketFragmentation()
|
||||
.Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
|
||||
await mqttClient.DisconnectAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Disconnect_Clean()
|
||||
{
|
||||
/*
|
||||
@@ -291,18 +314,19 @@ public static class Client_Connection_Samples
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io", 8883)
|
||||
.WithTls(
|
||||
.WithTlsOptions(
|
||||
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;
|
||||
};
|
||||
o.WithCertificateValidationHandler(
|
||||
eventArgs =>
|
||||
{
|
||||
eventArgs.Certificate.Subject.DumpToConsole();
|
||||
eventArgs.Certificate.GetExpirationDateString().DumpToConsole();
|
||||
eventArgs.Chain.ChainPolicy.RevocationMode.DumpToConsole();
|
||||
eventArgs.Chain.ChainStatus.DumpToConsole();
|
||||
eventArgs.SslPolicyErrors.DumpToConsole();
|
||||
return true;
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
@@ -406,6 +430,9 @@ public static class Client_Connection_Samples
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Console.WriteLine("Press <Enter> to exit");
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable UnusedMember.Local
|
||||
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Packets;
|
||||
@@ -176,4 +177,60 @@ public static class Client_Subscribe_Samples
|
||||
response.DumpToConsole();
|
||||
}
|
||||
}
|
||||
|
||||
static void ConcurrentProcessingDisableAutoAcknowledge(CancellationToken shutdownToken, IMqttClient mqttClient)
|
||||
{
|
||||
/*
|
||||
* This sample shows how to achieve concurrent processing and not have message AutoAcknowledged
|
||||
* This to have a proper QoS1 (at-least-once) experience for what at least MQTT specification can provide
|
||||
*/
|
||||
mqttClient.ApplicationMessageReceivedAsync += async ea =>
|
||||
{
|
||||
ea.AutoAcknowledge = false;
|
||||
|
||||
async Task ProcessAsync()
|
||||
{
|
||||
// DO YOUR WORK HERE!
|
||||
await Task.Delay(1000, shutdownToken);
|
||||
await ea.AcknowledgeAsync(shutdownToken);
|
||||
// WARNING: If process failures are not transient the message will be retried on every restart of the client
|
||||
// A failed message will not be dispatched again to the client as MQTT does not have a NACK packet to let
|
||||
// the broker know processing failed
|
||||
//
|
||||
// Optionally: Use a framework like Polly to create a retry policy: https://github.com/App-vNext/Polly#retry
|
||||
}
|
||||
|
||||
_ = Task.Run(ProcessAsync, shutdownToken);
|
||||
};
|
||||
}
|
||||
|
||||
static void ConcurrentProcessingWithLimit(CancellationToken shutdownToken, IMqttClient mqttClient)
|
||||
{
|
||||
/*
|
||||
* This sample shows how to achieve concurrent processing, with:
|
||||
* - a maximum concurrency limit based on Environment.ProcessorCount
|
||||
*/
|
||||
|
||||
var concurrent = new SemaphoreSlim(Environment.ProcessorCount);
|
||||
|
||||
mqttClient.ApplicationMessageReceivedAsync += async ea =>
|
||||
{
|
||||
await concurrent.WaitAsync(shutdownToken).ConfigureAwait(false);
|
||||
|
||||
async Task ProcessAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// DO YOUR WORK HERE!
|
||||
await Task.Delay(1000, shutdownToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
concurrent.Release();
|
||||
}
|
||||
}
|
||||
|
||||
_ = Task.Run(ProcessAsync, shutdownToken);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ public static class PackageInspection_Samples
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
|
||||
mqttClient.InspectPackage += OnInspectPackage;
|
||||
mqttClient.InspectPacketAsync += OnInspectPacket;
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
@@ -39,7 +39,7 @@ public static class PackageInspection_Samples
|
||||
}
|
||||
}
|
||||
|
||||
static Task OnInspectPackage(InspectMqttPacketEventArgs eventArgs)
|
||||
static Task OnInspectPacket(InspectMqttPacketEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Direction == MqttPacketFlowDirection.Inbound)
|
||||
{
|
||||
|
||||
+18
-1
@@ -4,7 +4,13 @@
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
Console.WriteLine("Welcome to MQTTnet samples!");
|
||||
Console.BackgroundColor = ConsoleColor.White;
|
||||
Console.BackgroundColor = ConsoleColor.Red;
|
||||
Console.Title = "Samples - MQTTnet";
|
||||
Console.WriteLine("----------------------------------------------");
|
||||
Console.WriteLine("- Welcome to MQTTnet samples! -");
|
||||
Console.WriteLine("----------------------------------------------");
|
||||
Console.ResetColor();
|
||||
Console.WriteLine();
|
||||
|
||||
var sampleClasses = Assembly.GetExecutingAssembly().GetExportedTypes().OrderBy(c => c.Name).ToList();
|
||||
@@ -16,7 +22,11 @@ foreach (var sampleClass in sampleClasses)
|
||||
index++;
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.Write("Please choose sample class (press Enter to continue): ");
|
||||
Console.ResetColor();
|
||||
|
||||
var input = Console.ReadLine();
|
||||
var selectedIndex = int.Parse(input ?? "0");
|
||||
var selectedSampleClass = sampleClasses[selectedIndex];
|
||||
@@ -29,12 +39,19 @@ foreach (var sampleMethod in sampleMethods)
|
||||
index++;
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.Write("Please choose sample (press Enter to continue): ");
|
||||
Console.ResetColor();
|
||||
|
||||
input = Console.ReadLine();
|
||||
selectedIndex = int.Parse(input ?? "0");
|
||||
var selectedSampleMethod = sampleMethods[selectedIndex];
|
||||
|
||||
Console.WriteLine();
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine("Executing sample...");
|
||||
Console.ResetColor();
|
||||
Console.WriteLine();
|
||||
|
||||
try
|
||||
|
||||
@@ -17,7 +17,7 @@ using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
|
||||
public static class Server_Asp_Net_Samples
|
||||
public static class Server_ASP_NET_Samples
|
||||
{
|
||||
public static Task Start_Server_With_WebSockets_Support()
|
||||
{
|
||||
@@ -7,6 +7,8 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System.Text.Json;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
|
||||
@@ -32,7 +34,10 @@ public static class Server_Retained_Messages_Samples
|
||||
{
|
||||
try
|
||||
{
|
||||
eventArgs.LoadedRetainedMessages = await JsonSerializer.DeserializeAsync<List<MqttApplicationMessage>>(File.OpenRead(storePath));
|
||||
var models = await JsonSerializer.DeserializeAsync<List<MqttRetainedMessageModel>>(File.OpenRead(storePath)) ?? new List<MqttRetainedMessageModel>();
|
||||
var retainedMessages = models.Select(m => m.ToApplicationMessage()).ToList();
|
||||
|
||||
eventArgs.LoadedRetainedMessages = retainedMessages;
|
||||
Console.WriteLine("Retained messages loaded.");
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
@@ -56,7 +61,9 @@ public static class Server_Retained_Messages_Samples
|
||||
// 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);
|
||||
var models = eventArgs.StoredRetainedMessages.Select(MqttRetainedMessageModel.Create);
|
||||
|
||||
var buffer = JsonSerializer.SerializeToUtf8Bytes(models);
|
||||
await File.WriteAllBytesAsync(storePath, buffer);
|
||||
Console.WriteLine("Retained messages saved.");
|
||||
}
|
||||
@@ -79,4 +86,59 @@ public static class Server_Retained_Messages_Samples
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
sealed class MqttRetainedMessageModel
|
||||
{
|
||||
public string? ContentType { get; set; }
|
||||
public byte[]? CorrelationData { get; set; }
|
||||
public byte[]? Payload { get; set; }
|
||||
public MqttPayloadFormatIndicator PayloadFormatIndicator { get; set; }
|
||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||
public string? ResponseTopic { get; set; }
|
||||
public string? Topic { get; set; }
|
||||
public List<MqttUserProperty>? UserProperties { get; set; }
|
||||
|
||||
public static MqttRetainedMessageModel Create(MqttApplicationMessage message)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(message));
|
||||
}
|
||||
|
||||
return new MqttRetainedMessageModel
|
||||
{
|
||||
Topic = message.Topic,
|
||||
|
||||
// Create a copy of the buffer from the payload segment because
|
||||
// it cannot be serialized and deserialized with the JSON serializer.
|
||||
Payload = message.PayloadSegment.ToArray(),
|
||||
UserProperties = message.UserProperties,
|
||||
ResponseTopic = message.ResponseTopic,
|
||||
CorrelationData = message.CorrelationData,
|
||||
ContentType = message.ContentType,
|
||||
PayloadFormatIndicator = message.PayloadFormatIndicator,
|
||||
QualityOfServiceLevel = message.QualityOfServiceLevel
|
||||
|
||||
// Other properties like "Retain" are not if interest in the storage.
|
||||
// That's why a custom model makes sense.
|
||||
};
|
||||
}
|
||||
|
||||
public MqttApplicationMessage ToApplicationMessage()
|
||||
{
|
||||
return new MqttApplicationMessage
|
||||
{
|
||||
Topic = Topic,
|
||||
PayloadSegment = new ArraySegment<byte>(Payload ?? Array.Empty<byte>()),
|
||||
PayloadFormatIndicator = PayloadFormatIndicator,
|
||||
ResponseTopic = ResponseTopic,
|
||||
CorrelationData = CorrelationData,
|
||||
ContentType = ContentType,
|
||||
UserProperties = UserProperties,
|
||||
QualityOfServiceLevel = QualityOfServiceLevel,
|
||||
Dup = false,
|
||||
Retain = true
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
|
||||
public static class Server_TLS_Samples
|
||||
{
|
||||
public static async Task Run_Server_With_Self_Signed_Certificate()
|
||||
{
|
||||
/*
|
||||
* This sample starts a simple MQTT server which will accept any TCP connection.
|
||||
* It also has an encrypted connection using a self signed TLS certificate.
|
||||
*
|
||||
* See sample "Run_Minimal_Server" for more details.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttFactory();
|
||||
|
||||
// This certificate is self signed so that
|
||||
var certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
|
||||
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(certificate).WithEncryptedEndpoint().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();
|
||||
}
|
||||
}
|
||||
|
||||
static X509Certificate2 CreateSelfSignedCertificate(string oid)
|
||||
{
|
||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
||||
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
||||
sanBuilder.AddDnsName("localhost");
|
||||
|
||||
using (var rsa = RSA.Create())
|
||||
{
|
||||
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||
|
||||
certRequest.CertificateExtensions.Add(
|
||||
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new(oid) }, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||
|
||||
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
|
||||
{
|
||||
var pfxCertificate = new X509Certificate2(
|
||||
certificate.Export(X509ContentType.Pfx),
|
||||
(string)null!,
|
||||
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
||||
|
||||
return pfxCertificate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ namespace MQTTnet.AspNetCore.Tests
|
||||
connection.Transport = pipe;
|
||||
var ctx = new MqttConnectionContext(serializer, connection);
|
||||
|
||||
await ctx.SendPacketAsync(new MqttPublishPacket { Payload = new byte[20_000] }, CancellationToken.None).ConfigureAwait(false);
|
||||
await ctx.SendPacketAsync(new MqttPublishPacket { PayloadSegment = new ArraySegment<byte>(new byte[20_000]) }, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
var readResult = await pipe.Send.Reader.ReadAsync();
|
||||
Assert.IsTrue(readResult.Buffer.Length > 20000);
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace MQTTnet.AspNetCore.Tests
|
||||
{
|
||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||
|
||||
var buffer = serializer.Encode(new MqttPublishPacket {Topic = "a", Payload = new byte[5]}).Join();
|
||||
var buffer = serializer.Encode(new MqttPublishPacket {Topic = "a", PayloadSegment = new byte[5]}).Join();
|
||||
|
||||
var sequence = new ReadOnlySequence<byte>(buffer.Array, buffer.Offset, buffer.Count);
|
||||
|
||||
|
||||
@@ -33,8 +33,11 @@ namespace MQTTnet.AspNetCore
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||
|
||||
_input = connection.Transport.Input;
|
||||
_output = connection.Transport.Output;
|
||||
if (!(_connection is TcpConnection tcp) || tcp.IsConnected)
|
||||
{
|
||||
_input = connection.Transport.Input;
|
||||
_output = connection.Transport.Output;
|
||||
}
|
||||
}
|
||||
|
||||
public long BytesReceived { get; private set; }
|
||||
|
||||
@@ -2,38 +2,34 @@
|
||||
// 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.AspNetCore.Http;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
|
||||
{
|
||||
IMqttNetLogger _logger = new MqttNetNullLogger();
|
||||
|
||||
IMqttNetLogger _logger = MqttNetNullLogger.Instance;
|
||||
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||
public void Dispose()
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
||||
{
|
||||
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
|
||||
if (webSocket == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(webSocket));
|
||||
}
|
||||
|
||||
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
|
||||
|
||||
@@ -48,7 +44,7 @@ namespace MQTTnet.AspNetCore
|
||||
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
|
||||
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
|
||||
|
||||
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, null, _logger))
|
||||
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger))
|
||||
{
|
||||
await clientHandler(channelAdapter).ConfigureAwait(false);
|
||||
}
|
||||
@@ -60,8 +56,15 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ namespace MQTTnet.Benchmarks
|
||||
|
||||
var channel = new MemoryMqttChannel(_stream);
|
||||
|
||||
_channelAdapter = new MqttChannelAdapter(channel, serializer, null, new MqttNetEventLogger());
|
||||
_channelAdapter = new MqttChannelAdapter(channel, serializer, new MqttNetEventLogger());
|
||||
}
|
||||
|
||||
static byte[] Join(params ArraySegment<byte>[] chunks)
|
||||
|
||||
@@ -30,17 +30,17 @@ namespace MQTTnet.Benchmarks
|
||||
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteBinaryData(_demoPayload);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
writer.WriteByte(0x01);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteVariableByteInteger(5647382);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteVariableByteInteger(8574489);
|
||||
writer.WriteBinaryData(_demoPayload);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
writer.WriteByte(2);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||
writer.WriteBinaryData(_demoPayload);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
|
||||
_readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
||||
}
|
||||
@@ -83,11 +83,11 @@ namespace MQTTnet.Benchmarks
|
||||
writer.WriteVariableByteInteger(5647382);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteVariableByteInteger(8574589);
|
||||
writer.WriteBinaryData(_demoPayload);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
writer.WriteByte(2);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||
writer.WriteBinaryData(_demoPayload);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
|
||||
writer.Reset(0);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace MQTTnet.Benchmarks
|
||||
public void Deserialize_10000_Messages()
|
||||
{
|
||||
var channel = new BenchmarkMqttChannel(_serializedPacket);
|
||||
var reader = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)), null, new MqttNetEventLogger());
|
||||
var reader = new MqttChannelAdapter(channel, new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535)), new MqttNetEventLogger());
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
|
||||
<AssemblyName>MQTTnet.Extensions.ManagedClient</AssemblyName>
|
||||
|
||||
@@ -353,13 +353,13 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
return remainingTime < TimeSpan.Zero ? TimeSpan.Zero : remainingTime;
|
||||
}
|
||||
|
||||
async Task HandleSubscriptionExceptionAsync(Exception exception)
|
||||
async Task HandleSubscriptionExceptionAsync(Exception exception, List<MqttTopicFilter> addedSubscriptions, List<string> removedSubscriptions)
|
||||
{
|
||||
_logger.Warning(exception, "Synchronizing subscriptions failed.");
|
||||
|
||||
if (_synchronizingSubscriptionsFailedEvent.HasHandlers)
|
||||
{
|
||||
await _synchronizingSubscriptionsFailedEvent.InvokeAsync(new ManagedProcessFailedEventArgs(exception)).ConfigureAwait(false);
|
||||
await _synchronizingSubscriptionsFailedEvent.InvokeAsync(new ManagedProcessFailedEventArgs(exception, addedSubscriptions, removedSubscriptions)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,11 +457,13 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
_logger.Info("Publishing subscriptions at reconnect");
|
||||
|
||||
List<MqttTopicFilter> topicFilters = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (_reconnectSubscriptions.Any())
|
||||
{
|
||||
var topicFilters = new List<MqttTopicFilter>();
|
||||
topicFilters = new List<MqttTopicFilter>();
|
||||
|
||||
foreach (var sub in _reconnectSubscriptions)
|
||||
{
|
||||
@@ -479,7 +481,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
|
||||
await HandleSubscriptionExceptionAsync(exception, topicFilters, null).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,6 +593,9 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
|
||||
await InternalClient.UnsubscribeAsync(unsubscribeOptionsBuilder.Build()).ConfigureAwait(false);
|
||||
|
||||
//clear because these worked, maybe the subscribe below will fail, only report those
|
||||
removedSubscriptions.Clear();
|
||||
}
|
||||
|
||||
if (addedSubscriptions != null && addedSubscriptions.Any())
|
||||
@@ -607,7 +612,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
await HandleSubscriptionExceptionAsync(exception).ConfigureAwait(false);
|
||||
await HandleSubscriptionExceptionAsync(exception, addedSubscriptions, removedSubscriptions).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
|
||||
/// <summary>
|
||||
/// Defines the maximum amount of topic filters which will be sent in a SUBSCRIBE/UNSUBSCRIBE packet.
|
||||
/// Amazon AWS limits this number to 8. The default is int.MaxValue.
|
||||
/// Amazon Web Services (AWS) limits this number to 8. The default is int.MaxValue.
|
||||
/// </summary>
|
||||
public int MaxTopicFiltersInSubscribeUnsubscribePackets { get; set; } = int.MaxValue;
|
||||
}
|
||||
|
||||
@@ -2,17 +2,33 @@
|
||||
// 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.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public class ManagedProcessFailedEventArgs : EventArgs
|
||||
{
|
||||
public ManagedProcessFailedEventArgs(Exception exception)
|
||||
public ManagedProcessFailedEventArgs(Exception exception, List<MqttTopicFilter> addedSubscriptions, List<string> removedSubscriptions)
|
||||
{
|
||||
Exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
||||
|
||||
if (addedSubscriptions != null)
|
||||
{
|
||||
AddedSubscriptions = new List<string>(addedSubscriptions.Select(item => item.Topic));
|
||||
}
|
||||
|
||||
if (removedSubscriptions != null)
|
||||
{
|
||||
RemovedSubscriptions = new List<string>(removedSubscriptions);
|
||||
}
|
||||
}
|
||||
|
||||
public Exception Exception { get; }
|
||||
|
||||
public List<string> AddedSubscriptions { get; }
|
||||
public List<string> RemovedSubscriptions { get; }
|
||||
}
|
||||
}
|
||||
@@ -12,22 +12,37 @@ namespace MQTTnet.Extensions.ManagedClient
|
||||
{
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttClient mqttClient = null)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
if (mqttClient == null)
|
||||
{
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(), factory.DefaultLogger);
|
||||
}
|
||||
|
||||
|
||||
return new ManagedMqttClient(mqttClient, factory.DefaultLogger);
|
||||
}
|
||||
|
||||
|
||||
public static IManagedMqttClient CreateManagedMqttClient(this MqttFactory factory, IMqttNetLogger logger)
|
||||
{
|
||||
if (factory == null) throw new ArgumentNullException(nameof(factory));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
return new ManagedMqttClient(factory.CreateMqttClient(logger), logger);
|
||||
}
|
||||
|
||||
public static ManagedMqttClientOptionsBuilder CreateManagedMqttClientOptionsBuilder(this MqttFactory _)
|
||||
{
|
||||
return new ManagedMqttClientOptionsBuilder();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Protocol;
|
||||
@@ -7,8 +8,8 @@ namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
public interface IMqttRpcClient : IDisposable
|
||||
{
|
||||
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel);
|
||||
Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string,object> parameters = null);
|
||||
|
||||
Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken = default);
|
||||
Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string, object> parameters = null, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
|
||||
<AssemblyName>MQTTnet.Extensions.Rpc</AssemblyName>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client;
|
||||
@@ -41,13 +43,13 @@ namespace MQTTnet.Extensions.Rpc
|
||||
_waitingCalls.Clear();
|
||||
}
|
||||
|
||||
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string, object> parameters = null)
|
||||
{
|
||||
using (var timeoutToken = new CancellationTokenSource(timeout))
|
||||
{
|
||||
try
|
||||
{
|
||||
return await ExecuteAsync(methodName, payload, qualityOfServiceLevel, timeoutToken.Token).ConfigureAwait(false);
|
||||
return await ExecuteAsync(methodName, payload, qualityOfServiceLevel, parameters, timeoutToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException exception)
|
||||
{
|
||||
@@ -61,14 +63,14 @@ namespace MQTTnet.Extensions.Rpc
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, CancellationToken cancellationToken = default)
|
||||
public async Task<byte[]> ExecuteAsync(string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string, object> parameters = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (methodName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(methodName));
|
||||
}
|
||||
|
||||
var context = new TopicGenerationContext(_mqttClient, _options, methodName, qualityOfServiceLevel);
|
||||
var context = new TopicGenerationContext(_mqttClient, _options, methodName, parameters, qualityOfServiceLevel);
|
||||
var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics(context);
|
||||
|
||||
var requestTopic = topicNames.RequestTopic;
|
||||
@@ -119,7 +121,6 @@ namespace MQTTnet.Extensions.Rpc
|
||||
finally
|
||||
{
|
||||
_waitingCalls.TryRemove(responseTopic, out _);
|
||||
|
||||
await _mqttClient.UnsubscribeAsync(responseTopic, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -131,7 +132,8 @@ namespace MQTTnet.Extensions.Rpc
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
awaitable.TrySetResult(eventArgs.ApplicationMessage.Payload);
|
||||
var payloadBuffer = eventArgs.ApplicationMessage.PayloadSegment.ToArray();
|
||||
awaitable.TrySetResult(payloadBuffer);
|
||||
|
||||
// Set this message to handled to that other code can avoid execution etc.
|
||||
eventArgs.IsHandled = true;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Protocol;
|
||||
@@ -11,13 +12,13 @@ namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
public static class MqttRpcClientExtensions
|
||||
{
|
||||
public static Task<byte[]> ExecuteAsync(this IMqttRpcClient client, TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
public static Task<byte[]> ExecuteAsync(this IMqttRpcClient client, TimeSpan timeout, string methodName, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, IDictionary<string,object> parameters = null)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
var buffer = Encoding.UTF8.GetBytes(payload ?? string.Empty);
|
||||
|
||||
return client.ExecuteAsync(timeout, methodName, buffer, qualityOfServiceLevel);
|
||||
return client.ExecuteAsync(timeout, methodName, buffer, qualityOfServiceLevel, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
@@ -10,9 +11,11 @@ namespace MQTTnet.Extensions.Rpc
|
||||
{
|
||||
public sealed class TopicGenerationContext
|
||||
{
|
||||
public TopicGenerationContext(IMqttClient mqttClient, MqttRpcClientOptions options, string methodName, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
public TopicGenerationContext(IMqttClient mqttClient, MqttRpcClientOptions options, string methodName,
|
||||
IDictionary<string, object> parameters, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
MethodName = methodName ?? throw new ArgumentNullException(nameof(methodName));
|
||||
Parameters = parameters;
|
||||
QualityOfServiceLevel = qualityOfServiceLevel;
|
||||
MqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
@@ -20,6 +23,8 @@ namespace MQTTnet.Extensions.Rpc
|
||||
|
||||
public string MethodName { get; }
|
||||
|
||||
public IDictionary<string, object> Parameters { get; }
|
||||
|
||||
public IMqttClient MqttClient { get; }
|
||||
|
||||
public MqttRpcClientOptions Options { get; }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net452;net461;net48</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">$(TargetFrameworks);uap10.0</TargetFrameworks>
|
||||
|
||||
<AssemblyName>MQTTnet.Extensions.WebSocket4Net</AssemblyName>
|
||||
|
||||
@@ -47,13 +47,13 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
||||
var uri = _webSocketOptions.Uri;
|
||||
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (_webSocketOptions.TlsOptions?.UseTls == false)
|
||||
if (_webSocketOptions.TlsOptions?.UseTls == true)
|
||||
{
|
||||
uri = "ws://" + uri;
|
||||
uri = "wss://" + uri;
|
||||
}
|
||||
else
|
||||
{
|
||||
uri = "wss://" + uri;
|
||||
uri = "ws://" + uri;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,18 +92,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
||||
var webSocketVersion = WebSocketVersion.None;
|
||||
var receiveBufferSize = 0;
|
||||
|
||||
var certificates = new X509CertificateCollection();
|
||||
if (_webSocketOptions.TlsOptions?.Certificates != null)
|
||||
{
|
||||
foreach (var certificate in _webSocketOptions.TlsOptions.Certificates)
|
||||
{
|
||||
#if WINDOWS_UWP
|
||||
certificates.Add(new X509Certificate(certificate));
|
||||
#else
|
||||
certificates.Add(certificate);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
var certificates = _webSocketOptions.TlsOptions?.ClientCertificatesProvider?.GetCertificates();
|
||||
|
||||
_webSocket = new WebSocket(uri, subProtocol, cookies, customHeaders, userAgent, origin, webSocketVersion, proxy, sslProtocols, receiveBufferSize)
|
||||
{
|
||||
|
||||
@@ -27,8 +27,10 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
||||
return new MqttChannelAdapter(
|
||||
new MqttTcpChannel(options),
|
||||
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
|
||||
packetInspector,
|
||||
logger);
|
||||
logger)
|
||||
{
|
||||
PacketInspector = packetInspector
|
||||
};
|
||||
}
|
||||
|
||||
case MqttClientWebSocketOptions webSocketOptions:
|
||||
@@ -36,8 +38,10 @@ namespace MQTTnet.Extensions.WebSocket4Net
|
||||
return new MqttChannelAdapter(
|
||||
new WebSocket4NetMqttChannel(options, webSocketOptions),
|
||||
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
|
||||
packetInspector,
|
||||
logger);
|
||||
logger)
|
||||
{
|
||||
PacketInspector = packetInspector
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -33,9 +33,18 @@ namespace MQTTnet.TestApp
|
||||
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
var payloadText = string.Empty;
|
||||
if (e.ApplicationMessage.PayloadSegment.Count > 0)
|
||||
{
|
||||
payloadText = Encoding.UTF8.GetString(
|
||||
e.ApplicationMessage.PayloadSegment.Array,
|
||||
e.ApplicationMessage.PayloadSegment.Offset,
|
||||
e.ApplicationMessage.PayloadSegment.Count);
|
||||
}
|
||||
|
||||
Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
|
||||
Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
|
||||
Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}");
|
||||
Console.WriteLine($"+ Payload = {payloadText}");
|
||||
Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
|
||||
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
|
||||
Console.WriteLine();
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace MQTTnet.TestApp
|
||||
return new MqttApplicationMessage
|
||||
{
|
||||
Topic = "A/B/C",
|
||||
Payload = Encoding.UTF8.GetBytes(Payload),
|
||||
PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(Payload)),
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce
|
||||
};
|
||||
}
|
||||
@@ -229,7 +229,7 @@ namespace MQTTnet.TestApp
|
||||
var message = new MqttApplicationMessage
|
||||
{
|
||||
Topic = "A/B/C",
|
||||
Payload = Encoding.UTF8.GetBytes("Hello World"),
|
||||
PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce
|
||||
};
|
||||
|
||||
@@ -280,7 +280,7 @@ namespace MQTTnet.TestApp
|
||||
var message = new MqttApplicationMessage
|
||||
{
|
||||
Topic = "A/B/C",
|
||||
Payload = Encoding.UTF8.GetBytes("Hello World"),
|
||||
PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce
|
||||
};
|
||||
|
||||
@@ -331,7 +331,7 @@ namespace MQTTnet.TestApp
|
||||
var message = new MqttApplicationMessage
|
||||
{
|
||||
Topic = "A/B/C",
|
||||
Payload = Encoding.UTF8.GetBytes("Hello World"),
|
||||
PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello World")),
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
||||
};
|
||||
|
||||
|
||||
@@ -20,21 +20,25 @@ namespace MQTTnet.TestApp
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
// TLS13 is only available in Net5.0
|
||||
var unsafeTls13 = new MqttClientOptionsBuilderTlsParameters
|
||||
var unsafeTls13 = new MqttClientTlsOptions
|
||||
{
|
||||
UseTls = true,
|
||||
SslProtocol = SslProtocols.Tls13,
|
||||
// Don't use this in production code. This handler simply allows any invalid certificate to work.
|
||||
CertificateValidationHandler = w => true
|
||||
AllowUntrustedCertificates = true,
|
||||
IgnoreCertificateChainErrors = true,
|
||||
CertificateValidationHandler = _ => true
|
||||
};
|
||||
#endif
|
||||
// Also defining TLS12 for servers that don't seem no to support TLS13.
|
||||
var unsafeTls12 = new MqttClientOptionsBuilderTlsParameters
|
||||
var unsafeTls12 = new MqttClientTlsOptions
|
||||
{
|
||||
UseTls = true,
|
||||
SslProtocol = SslProtocols.Tls12,
|
||||
// Don't use this in production code. This handler simply allows any invalid certificate to work.
|
||||
CertificateValidationHandler = w => true
|
||||
AllowUntrustedCertificates = true,
|
||||
IgnoreCertificateChainErrors = true,
|
||||
CertificateValidationHandler = _ => true
|
||||
};
|
||||
|
||||
// mqtt.eclipseprojects.io
|
||||
@@ -44,16 +48,16 @@ namespace MQTTnet.TestApp
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"mqtt.eclipseprojects.io WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:80/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("mqtt.eclipseprojects.io:80/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
await ExecuteTestAsync("mqtt.eclipseprojects.io WS TLS13",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:443/mqtt")
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("mqtt.eclipseprojects.io:443/mqtt"))
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls13).Build());
|
||||
|
||||
await ExecuteTestAsync("mqtt.eclipseprojects.io WS TLS13 (WebSocket4Net)",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:443/mqtt")
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build(),
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("mqtt.eclipseprojects.io:443/mqtt"))
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls13).Build(),
|
||||
true);
|
||||
#endif
|
||||
|
||||
@@ -68,12 +72,12 @@ namespace MQTTnet.TestApp
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"test.mosquitto.org TCP TLS12",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
await ExecuteTestAsync("test.mosquitto.org TCP TLS13",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls13).Build());
|
||||
#endif
|
||||
|
||||
await ExecuteTestAsync(
|
||||
@@ -81,26 +85,26 @@ namespace MQTTnet.TestApp
|
||||
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8885)
|
||||
.WithCredentials("rw", "readwrite")
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTls(unsafeTls12)
|
||||
.WithTlsOptions(unsafeTls12)
|
||||
.Build());
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"test.mosquitto.org WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8080/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("test.mosquitto.org:8080/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"test.mosquitto.org WS (WebSocket4Net)",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8080/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build(),
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("test.mosquitto.org:8080/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build(),
|
||||
true);
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"test.mosquitto.org WS TLS12",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("test.mosquitto.org:8081/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
// await ExecuteTestAsync(
|
||||
// "test.mosquitto.org WS TLS12 (WebSocket4Net)",
|
||||
// new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build(),
|
||||
// true);
|
||||
await ExecuteTestAsync(
|
||||
"test.mosquitto.org WS TLS12 (WebSocket4Net)",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("test.mosquitto.org:8081/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build(),
|
||||
true);
|
||||
|
||||
// broker.emqx.io
|
||||
await ExecuteTestAsync(
|
||||
@@ -109,30 +113,30 @@ namespace MQTTnet.TestApp
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"broker.emqx.io TCP TLS12",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
await ExecuteTestAsync("broker.emqx.io TCP TLS13",
|
||||
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls13).Build());
|
||||
#endif
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"broker.emqx.io WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8083/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.emqx.io:8083/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"broker.emqx.io WS (WebSocket4Net)",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8084/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build(),
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.emqx.io:8084/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build(),
|
||||
true);
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"broker.emqx.io WS TLS12",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8084/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.emqx.io:8084/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build());
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"broker.emqx.io WS TLS12 (WebSocket4Net)",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8084/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build(),
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.emqx.io:8084/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).WithTlsOptions(unsafeTls12).Build(),
|
||||
true);
|
||||
|
||||
// broker.hivemq.com
|
||||
@@ -142,15 +146,14 @@ namespace MQTTnet.TestApp
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"broker.hivemq.com WS",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.hivemq.com:8000/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||
|
||||
await ExecuteTestAsync(
|
||||
"broker.hivemq.com WS (WebSocket4Net)",
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build(),
|
||||
new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.hivemq.com:8000/mqtt")).WithProtocolVersion(MqttProtocolVersion.V311).Build(),
|
||||
true);
|
||||
|
||||
// mqtt.swifitch.cz: Does not seem to operate any more
|
||||
|
||||
// cloudmqtt.com: Cannot test because it does not offer a free plan any more.
|
||||
|
||||
Write("Finished.", ConsoleColor.White);
|
||||
@@ -197,9 +200,10 @@ namespace MQTTnet.TestApp
|
||||
|
||||
Write("[OK]\n", ConsoleColor.Green);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception exception)
|
||||
{
|
||||
Write("[FAILED] " + e.Message + "\n", ConsoleColor.Red);
|
||||
Write("[FAILED]" + Environment.NewLine, ConsoleColor.Red);
|
||||
Write(exception + Environment.NewLine, ConsoleColor.Red);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics;
|
||||
@@ -100,7 +101,7 @@ namespace MQTTnet.TestApp
|
||||
{
|
||||
// Replace the payload with the timestamp. But also extending a JSON
|
||||
// based payload with the timestamp is a suitable use case.
|
||||
e.ApplicationMessage.Payload = Encoding.UTF8.GetBytes(DateTime.Now.ToString("O"));
|
||||
e.ApplicationMessage.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(DateTime.Now.ToString("O")));
|
||||
}
|
||||
|
||||
if (e.ApplicationMessage.Topic == "not_allowed_topic")
|
||||
@@ -143,10 +144,16 @@ namespace MQTTnet.TestApp
|
||||
|
||||
mqttServer.InterceptingPublishAsync += e =>
|
||||
{
|
||||
MqttNetConsoleLogger.PrintToConsole(
|
||||
$"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0])}'",
|
||||
ConsoleColor.Magenta);
|
||||
var payloadText = string.Empty;
|
||||
if (e.ApplicationMessage.PayloadSegment.Count > 0)
|
||||
{
|
||||
payloadText = Encoding.UTF8.GetString(
|
||||
e.ApplicationMessage.PayloadSegment.Array,
|
||||
e.ApplicationMessage.PayloadSegment.Offset,
|
||||
e.ApplicationMessage.PayloadSegment.Count);
|
||||
}
|
||||
|
||||
MqttNetConsoleLogger.PrintToConsole($"'{e.ClientId}' reported '{e.ApplicationMessage.Topic}' > '{payloadText}'", ConsoleColor.Magenta);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,38 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
||||
[TestClass]
|
||||
public sealed class ManagedMqttClient_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Expose_Custom_Connection_Error()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += args =>
|
||||
{
|
||||
args.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient();
|
||||
|
||||
MqttClientDisconnectedEventArgs disconnectedArgs = null;
|
||||
managedClient.DisconnectedAsync += args =>
|
||||
{
|
||||
disconnectedArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var clientOptions = testEnvironment.Factory.CreateManagedMqttClientOptionsBuilder().WithClientOptions(testEnvironment.CreateDefaultClientOptions()).Build();
|
||||
await managedClient.StartAsync(clientOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(disconnectedArgs);
|
||||
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, disconnectedArgs.ConnectResult.ResultCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_While_Not_Cleanly_Disconnected()
|
||||
{
|
||||
@@ -389,7 +421,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
||||
var receivingClient = await CreateManagedClientAsync(testEnvironment, null, connectionCheckInterval);
|
||||
var sendingClient = await testEnvironment.ConnectClient();
|
||||
|
||||
await sendingClient.PublishAsync(new MqttApplicationMessage { Topic = "topic", Payload = new byte[] { 1 }, Retain = true });
|
||||
await sendingClient.PublishAsync(new MqttApplicationMessage { Topic = "topic", PayloadSegment = new ArraySegment<byte>( new byte[] { 1 }), Retain = true });
|
||||
|
||||
var subscribeTime = DateTime.UtcNow;
|
||||
|
||||
@@ -422,7 +454,7 @@ namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
||||
//wait a bit for the subscription to become established
|
||||
await Task.Delay(500);
|
||||
|
||||
await sendingClient.PublishAsync(new MqttApplicationMessage { Topic = "topic", Payload = new byte[] { 1 }, Retain = true });
|
||||
await sendingClient.PublishAsync(new MqttApplicationMessage { Topic = "topic", PayloadSegment = new ArraySegment<byte>(new byte[] { 1 }), Retain = true });
|
||||
|
||||
var messages = await SetupReceivingOfMessages(managedClient, 1);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -10,6 +11,7 @@ using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
@@ -99,6 +101,8 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
|
||||
}
|
||||
@@ -125,6 +129,8 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.AreEqual(MqttDisconnectReasonCode.MessageRateTooHigh, eventArgs.ReasonCode);
|
||||
}
|
||||
@@ -150,7 +156,9 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
||||
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsNotNull(eventArgs.UserProperties);
|
||||
Assert.AreEqual(1, eventArgs.UserProperties.Count);
|
||||
@@ -158,5 +166,57 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
||||
Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Return_Non_Success()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += args =>
|
||||
{
|
||||
args.ResponseUserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Property", "Value")
|
||||
};
|
||||
|
||||
args.ReasonCode = MqttConnectReasonCode.QuotaExceeded;
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var response = await client.ConnectAsync(testEnvironment.CreateDefaultClientOptionsBuilder().WithoutThrowOnNonSuccessfulConnectResponse().Build());
|
||||
|
||||
Assert.IsNotNull(response);
|
||||
Assert.AreEqual(MqttClientConnectResultCode.QuotaExceeded, response.ResultCode);
|
||||
Assert.AreEqual(response.UserProperties[0].Name, "Property");
|
||||
Assert.AreEqual(response.UserProperties[0].Value, "Value");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Throw_Proper_Exception_When_Not_Connected()
|
||||
{
|
||||
try
|
||||
{
|
||||
var mqttFactory = new MqttFactory();
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
await mqttClient.SubscribeAsync("test", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
{
|
||||
if (exception.Message == "The client is not connected.")
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,7 +296,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
||||
|
||||
Assert.IsNotNull(receivedMessage);
|
||||
Assert.AreEqual("A", receivedMessage.Topic);
|
||||
Assert.AreEqual(null, receivedMessage.Payload);
|
||||
Assert.AreEqual(null, receivedMessage.PayloadSegment.Array);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,7 +507,7 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
||||
|
||||
client2.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
|
||||
client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.PayloadSegment.ToArray()));
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace MQTTnet.Tests.Diagnostics
|
||||
|
||||
var packets = new List<string>();
|
||||
|
||||
mqttClient.InspectPackage += eventArgs =>
|
||||
mqttClient.InspectPacketAsync += eventArgs =>
|
||||
{
|
||||
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
||||
return CompletedTask.Instance;
|
||||
|
||||
@@ -1,98 +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;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class Extension_Tests
|
||||
{
|
||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
||||
[TestMethod]
|
||||
public async Task TimeoutAfter()
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(500), ct),
|
||||
TimeSpan.FromMilliseconds(100), CancellationToken.None);
|
||||
}
|
||||
|
||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
||||
[TestMethod]
|
||||
public async Task TimeoutAfterWithResult()
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(
|
||||
ct => Task.Delay(TimeSpan.FromMilliseconds(500), ct).ContinueWith(t => 5, ct),
|
||||
TimeSpan.FromMilliseconds(100), CancellationToken.None);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TimeoutAfterWithInnerException()
|
||||
{
|
||||
try
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(ct => Task.Run(() =>
|
||||
{
|
||||
var iis = new int[0];
|
||||
iis[1] = 0;
|
||||
}, ct), TimeSpan.FromSeconds(1), CancellationToken.None);
|
||||
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Assert.IsTrue(e is IndexOutOfRangeException);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TimeoutAfterWithInnerExceptionWithResult()
|
||||
{
|
||||
try
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(ct => Task.Run(() =>
|
||||
{
|
||||
var iis = new int[0];
|
||||
iis[1] = 0;
|
||||
return iis[0];
|
||||
}, ct), TimeSpan.FromSeconds(1), CancellationToken.None);
|
||||
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Assert.IsTrue(e is IndexOutOfRangeException);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TimeoutAfterMemoryUsage()
|
||||
{
|
||||
var initialMemory = GC.GetTotalMemory(true);
|
||||
|
||||
var tasks = Enumerable.Range(0, 100000)
|
||||
.Select(i =>
|
||||
{
|
||||
return MqttTaskTimeout.WaitAsync(ct => Task.Delay(TimeSpan.FromMilliseconds(1), ct),
|
||||
TimeSpan.FromMinutes(1), CancellationToken.None);
|
||||
});
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
AssertIsLess(initialMemory + 2_000_000, GC.GetTotalMemory(true));
|
||||
}
|
||||
|
||||
static void AssertIsLess(long bound, long actual)
|
||||
{
|
||||
if (bound < actual)
|
||||
{
|
||||
Assert.Fail($"value must be less than {bound:N0} but is {actual:N0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -45,6 +46,34 @@ namespace MQTTnet.Tests.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Execute_Success_Parameters_Propagated_Correctly()
|
||||
{
|
||||
var paramValue = "123";
|
||||
var parameters = new Dictionary<string, object>
|
||||
{
|
||||
{ TestParametersTopicGenerationStrategy.ExpectedParamName, "123" },
|
||||
};
|
||||
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var responseSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder());
|
||||
await responseSender.SubscribeAsync($"MQTTnet.RPC/+/ping/{paramValue}");
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += e => responseSender.PublishStringAsync(e.ApplicationMessage.Topic + "/response", "pong");
|
||||
|
||||
using (var rpcClient = await testEnvironment.ConnectRpcClient(new MqttRpcClientOptionsBuilder()
|
||||
.WithTopicGenerationStrategy(new TestParametersTopicGenerationStrategy()).Build()))
|
||||
{
|
||||
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", MqttQualityOfServiceLevel.AtMostOnce, parameters);
|
||||
|
||||
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_0()
|
||||
{
|
||||
@@ -222,5 +251,32 @@ namespace MQTTnet.Tests.Extensions
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class TestParametersTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
|
||||
{
|
||||
internal const string ExpectedParamName = "test_param_name";
|
||||
|
||||
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
|
||||
{
|
||||
if (context.Parameters == null)
|
||||
{
|
||||
throw new InvalidOperationException("Parameters dictionary expected to be not null");
|
||||
}
|
||||
|
||||
if (!context.Parameters.TryGetValue(ExpectedParamName, out var paramValue))
|
||||
{
|
||||
throw new InvalidOperationException($"Parameter with name {ExpectedParamName} not present");
|
||||
}
|
||||
|
||||
var requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{context.MethodName}/{paramValue}";
|
||||
var responseTopic = requestTopic + "/response";
|
||||
|
||||
return new MqttRpcTopicPair
|
||||
{
|
||||
RequestTopic = requestTopic,
|
||||
ResponseTopic = responseTopic
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ namespace MQTTnet.Tests.Extensions
|
||||
|
||||
using (var client = factory.CreateMqttClient())
|
||||
{
|
||||
var options = new MqttClientOptionsBuilder().WithWebSocketServer("ws://a.b/mqtt").WithTimeout(TimeSpan.FromSeconds(2)).Build();
|
||||
var options = new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("ws://a.b/mqtt")).WithTimeout(TimeSpan.FromSeconds(2)).Build();
|
||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace MQTTnet.Tests.Formatter
|
||||
PacketIdentifier = 123,
|
||||
Dup = true,
|
||||
Retain = true,
|
||||
Payload = Encoding.ASCII.GetBytes("HELLO"),
|
||||
PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("HELLO")),
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||
Topic = "A/B/C"
|
||||
};
|
||||
@@ -318,10 +318,9 @@ namespace MQTTnet.Tests.Formatter
|
||||
var publishPacket = new MqttPublishPacket
|
||||
{
|
||||
Topic = "abcdefghijklmnopqrstuvwxyz0123456789",
|
||||
Payload = payload
|
||||
PayloadSegment = new ArraySegment<byte>(payload)
|
||||
};
|
||||
|
||||
|
||||
var serializationHelper = new MqttPacketSerializationHelper();
|
||||
|
||||
var buffer = serializationHelper.Encode(publishPacket);
|
||||
@@ -329,17 +328,17 @@ namespace MQTTnet.Tests.Formatter
|
||||
|
||||
Assert.IsNotNull(publishPacketCopy);
|
||||
Assert.AreEqual(publishPacket.Topic, publishPacketCopy.Topic);
|
||||
CollectionAssert.AreEqual(publishPacket.Payload, publishPacketCopy.Payload);
|
||||
CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), publishPacketCopy.PayloadSegment.ToArray());
|
||||
|
||||
// Now modify the payload and test again.
|
||||
publishPacket.Payload = Encoding.UTF8.GetBytes("MQTT");
|
||||
publishPacket.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes("MQTT"));
|
||||
|
||||
buffer = serializationHelper.Encode(publishPacket);
|
||||
var publishPacketCopy2 = serializationHelper.Decode(buffer) as MqttPublishPacket;
|
||||
|
||||
Assert.IsNotNull(publishPacketCopy2);
|
||||
Assert.AreEqual(publishPacket.Topic, publishPacketCopy2.Topic);
|
||||
CollectionAssert.AreEqual(publishPacket.Payload, publishPacketCopy2.Payload);
|
||||
CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), publishPacketCopy2.PayloadSegment.ToArray());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -463,7 +462,7 @@ namespace MQTTnet.Tests.Formatter
|
||||
PacketIdentifier = 123,
|
||||
Dup = true,
|
||||
Retain = true,
|
||||
Payload = Encoding.ASCII.GetBytes("HELLO"),
|
||||
PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("HELLO")),
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||
Topic = "A/B/C"
|
||||
};
|
||||
@@ -567,7 +566,6 @@ namespace MQTTnet.Tests.Formatter
|
||||
using (var adapter = new MqttChannelAdapter(
|
||||
channel,
|
||||
new MqttPacketFormatterAdapter(protocolVersion, new MqttBufferWriter(4096, 65535)),
|
||||
null,
|
||||
new MqttNetEventLogger()))
|
||||
{
|
||||
var receivedPacket = adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||
@@ -583,19 +581,12 @@ namespace MQTTnet.Tests.Formatter
|
||||
MqttProtocolVersion DeserializeAndDetectVersion(MqttPacketFormatterAdapter packetFormatterAdapter, byte[] buffer)
|
||||
{
|
||||
var channel = new MemoryMqttChannel(buffer);
|
||||
var adapter = new MqttChannelAdapter(channel, packetFormatterAdapter, null, new MqttNetEventLogger());
|
||||
var adapter = new MqttChannelAdapter(channel, packetFormatterAdapter, new MqttNetEventLogger());
|
||||
|
||||
adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||
return packetFormatterAdapter.ProtocolVersion;
|
||||
}
|
||||
|
||||
MqttBufferReader ReaderFactory(byte[] data)
|
||||
{
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(data, 0, data.Length);
|
||||
return reader;
|
||||
}
|
||||
|
||||
TPacket Roundtrip<TPacket>(TPacket packet, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311, MqttBufferWriter bufferWriter = null) where TPacket : MqttPacket
|
||||
{
|
||||
var writer = bufferWriter ?? WriterFactory();
|
||||
@@ -604,7 +595,7 @@ namespace MQTTnet.Tests.Formatter
|
||||
|
||||
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)), new MqttNetEventLogger());
|
||||
return (TPacket)adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// 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.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
@@ -76,7 +78,7 @@ namespace MQTTnet.Tests.Formatter
|
||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttConnAckPacket_V310()
|
||||
{
|
||||
var connAckPacket = new MqttConnAckPacket
|
||||
@@ -296,7 +298,7 @@ namespace MQTTnet.Tests.Formatter
|
||||
PacketIdentifier = 123,
|
||||
Dup = true,
|
||||
Retain = true,
|
||||
Payload = Encoding.ASCII.GetBytes("Payload"),
|
||||
PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("Payload")),
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||
Topic = "Topic",
|
||||
ResponseTopic = "/Response",
|
||||
@@ -320,7 +322,7 @@ namespace MQTTnet.Tests.Formatter
|
||||
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
|
||||
Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
|
||||
CollectionAssert.AreEqual(publishPacket.Payload, deserialized.Payload);
|
||||
CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), deserialized.PayloadSegment.ToArray());
|
||||
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
|
||||
Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
|
||||
Assert.AreEqual(null, deserialized.ResponseTopic); // Not supported in v3.1.1.
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// 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.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
@@ -257,7 +259,7 @@ namespace MQTTnet.Tests.Formatter
|
||||
PacketIdentifier = 123,
|
||||
Dup = true,
|
||||
Retain = true,
|
||||
Payload = Encoding.ASCII.GetBytes("Payload"),
|
||||
PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("Payload")),
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||
Topic = "Topic",
|
||||
ResponseTopic = "/Response",
|
||||
@@ -281,7 +283,7 @@ namespace MQTTnet.Tests.Formatter
|
||||
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
|
||||
Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
|
||||
CollectionAssert.AreEqual(publishPacket.Payload, deserialized.Payload);
|
||||
CollectionAssert.AreEqual(publishPacket.PayloadSegment.ToArray(), deserialized.PayloadSegment.ToArray());
|
||||
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
|
||||
Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
|
||||
Assert.AreEqual(publishPacket.ResponseTopic, deserialized.ResponseTopic);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Formatter
|
||||
@@ -47,23 +48,32 @@ namespace MQTTnet.Tests.Formatter
|
||||
Assert.AreEqual(1234U, reader.ReadVariableByteInteger());
|
||||
Assert.AreEqual(9876U, reader.ReadVariableByteInteger());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Throw_If_String_Too_Long()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
|
||||
writer.WriteString(string.Empty.PadLeft(65536));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Write_And_Read_Multiple_Times()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteBinaryData(new byte[1234]);
|
||||
writer.WriteBinary(new byte[1234]);
|
||||
writer.WriteByte(0x01);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteVariableByteInteger(5647382);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteVariableByteInteger(8574489);
|
||||
writer.WriteBinaryData(new byte[48]);
|
||||
writer.WriteBinary(new byte[48]);
|
||||
writer.WriteByte(2);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||
writer.WriteBinaryData(new byte[3]);
|
||||
writer.WriteBinary(new byte[3]);
|
||||
|
||||
var readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
||||
|
||||
|
||||
@@ -51,10 +51,7 @@ namespace MQTTnet.Tests.Mockups
|
||||
|
||||
public void AssertReceivedCountEquals(int expectedCount)
|
||||
{
|
||||
lock (_receivedEventArgs)
|
||||
{
|
||||
Assert.AreEqual(expectedCount, _receivedEventArgs.Count);
|
||||
}
|
||||
Assert.AreEqual(expectedCount, Count);
|
||||
}
|
||||
|
||||
public string GeneratePayloadSequence()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@@ -24,10 +25,9 @@ namespace MQTTnet.Tests.Mockups
|
||||
public sealed class TestEnvironment : IDisposable
|
||||
{
|
||||
readonly List<string> _clientErrors = new List<string>();
|
||||
readonly List<IMqttClient> _clients = new List<IMqttClient>();
|
||||
|
||||
readonly ConcurrentBag<IMqttClient> _clients = new ConcurrentBag<IMqttClient>();
|
||||
readonly List<Exception> _exceptions = new List<Exception>();
|
||||
readonly List<ILowLevelMqttClient> _lowLevelClients = new List<ILowLevelMqttClient>();
|
||||
readonly ConcurrentBag<ILowLevelMqttClient> _lowLevelClients = new ConcurrentBag<ILowLevelMqttClient>();
|
||||
readonly MqttProtocolVersion _protocolVersion;
|
||||
readonly List<string> _serverErrors = new List<string>();
|
||||
|
||||
@@ -65,9 +65,12 @@ namespace MQTTnet.Tests.Mockups
|
||||
|
||||
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
||||
{
|
||||
lock (_clientErrors)
|
||||
if (!IgnoreClientLogErrors)
|
||||
{
|
||||
_clientErrors.Add(e.LogMessage.ToString());
|
||||
lock (_clientErrors)
|
||||
{
|
||||
_clientErrors.Add(e.LogMessage.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -212,30 +215,27 @@ namespace MQTTnet.Tests.Mockups
|
||||
|
||||
public IMqttClient CreateClient()
|
||||
{
|
||||
lock (_clients)
|
||||
var logger = EnableLogger ? (IMqttNetLogger)ClientLogger : MqttNetNullLogger.Instance;
|
||||
|
||||
var client = Factory.CreateMqttClient(logger);
|
||||
client.ConnectingAsync += e =>
|
||||
{
|
||||
var logger = EnableLogger ? (IMqttNetLogger)ClientLogger : new MqttNetNullLogger();
|
||||
|
||||
var client = Factory.CreateMqttClient(logger);
|
||||
_clients.Add(client);
|
||||
|
||||
client.ConnectingAsync += e =>
|
||||
if (TestContext != null)
|
||||
{
|
||||
if (TestContext != null)
|
||||
var clientOptions = e.ClientOptions;
|
||||
var existingClientId = clientOptions.ClientId;
|
||||
if (existingClientId != null && !existingClientId.StartsWith(TestContext.TestName))
|
||||
{
|
||||
var clientOptions = e.ClientOptions;
|
||||
var existingClientId = clientOptions.ClientId;
|
||||
if (existingClientId != null && !existingClientId.StartsWith(TestContext.TestName))
|
||||
{
|
||||
clientOptions.ClientId = TestContext.TestName + "_" + existingClientId;
|
||||
}
|
||||
clientOptions.ClientId = TestContext.TestName + "_" + existingClientId;
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
return client;
|
||||
}
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
_clients.Add(client);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public MqttClientOptions CreateDefaultClientOptions()
|
||||
@@ -245,18 +245,15 @@ namespace MQTTnet.Tests.Mockups
|
||||
|
||||
public MqttClientOptionsBuilder CreateDefaultClientOptionsBuilder()
|
||||
{
|
||||
return Factory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion).WithTcpServer("127.0.0.1", ServerPort);
|
||||
return Factory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion).WithTcpServer("127.0.0.1", ServerPort).WithClientId(TestContext.TestName + "_" + Guid.NewGuid());
|
||||
}
|
||||
|
||||
public ILowLevelMqttClient CreateLowLevelClient()
|
||||
{
|
||||
lock (_clients)
|
||||
{
|
||||
var client = Factory.CreateLowLevelMqttClient(ClientLogger);
|
||||
_lowLevelClients.Add(client);
|
||||
var client = Factory.CreateLowLevelMqttClient(ClientLogger);
|
||||
_lowLevelClients.Add(client);
|
||||
|
||||
return client;
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
public MqttServer CreateServer(MqttServerOptions options)
|
||||
@@ -383,13 +380,16 @@ namespace MQTTnet.Tests.Mockups
|
||||
}
|
||||
}
|
||||
|
||||
lock (_clientErrors)
|
||||
if (!IgnoreClientLogErrors)
|
||||
{
|
||||
if (!IgnoreClientLogErrors && _clientErrors.Count > 0)
|
||||
lock (_clientErrors)
|
||||
{
|
||||
var message = $"Client(s) had {_clientErrors.Count} errors (${string.Join(Environment.NewLine, _clientErrors)})";
|
||||
Console.WriteLine(message);
|
||||
throw new Exception(message);
|
||||
if (_clientErrors.Count > 0)
|
||||
{
|
||||
var message = $"Client(s) had {_clientErrors.Count} errors (${string.Join(Environment.NewLine, _clientErrors)})";
|
||||
Console.WriteLine(message);
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Protocol;
|
||||
@@ -11,7 +12,7 @@ using MQTTnet.Protocol;
|
||||
namespace MQTTnet.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class MqttApplicationMessageBuilder_Tests
|
||||
public sealed class MqttApplicationMessageBuilder_Tests
|
||||
{
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_TopicOnly()
|
||||
@@ -29,7 +30,7 @@ namespace MQTTnet.Tests
|
||||
Assert.AreEqual("xyz", message.Topic);
|
||||
Assert.IsFalse(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(message.Payload), "00:06:00");
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(message.PayloadSegment.ToArray()), "00:06:00");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -41,7 +42,7 @@ namespace MQTTnet.Tests
|
||||
Assert.AreEqual("123", message.Topic);
|
||||
Assert.IsFalse(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(message.Payload), "Hello");
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(message.PayloadSegment.ToArray()), "Hello");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class MqttApplicationMessageTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void PayloadSegment()
|
||||
{
|
||||
var message = new MqttApplicationMessage();
|
||||
Assert.AreEqual(0, message.Payload.Length);
|
||||
Assert.AreEqual(0, message.PayloadSegment.Count);
|
||||
|
||||
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.Payload));
|
||||
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.PayloadSegment.Array));
|
||||
|
||||
message.Payload = new byte[] { 1, 2 };
|
||||
Assert.AreEqual(2, message.Payload.Length);
|
||||
Assert.AreEqual(2, message.PayloadSegment.Count);
|
||||
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.Payload));
|
||||
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.PayloadSegment.Array));
|
||||
|
||||
message.Payload = new byte[] { 1, 2, 3 };
|
||||
Assert.AreEqual(3, message.Payload.Length);
|
||||
Assert.AreEqual(3, message.PayloadSegment.Count);
|
||||
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.Payload));
|
||||
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.PayloadSegment.Array));
|
||||
|
||||
message.PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3 });
|
||||
Assert.AreEqual(3, message.Payload.Length);
|
||||
Assert.AreEqual(3, message.PayloadSegment.Count);
|
||||
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.Payload));
|
||||
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.PayloadSegment.Array));
|
||||
|
||||
message.PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3 }, 1, 1);
|
||||
Assert.AreEqual(1, message.Payload.Length);
|
||||
Assert.AreEqual(1, message.PayloadSegment.Count);
|
||||
Assert.IsTrue(object.ReferenceEquals(message.Payload, message.Payload));
|
||||
Assert.IsFalse(object.ReferenceEquals(message.Payload, message.PayloadSegment.Array));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ namespace MQTTnet.Tests
|
||||
{
|
||||
var formatterAdapter = new MqttPacketFormatterAdapter(_protocolVersion, new MqttBufferWriter(4096, 65535));
|
||||
|
||||
var adapter = new MqttChannelAdapter(channel, formatterAdapter, null, MqttNetNullLogger.Instance);
|
||||
var adapter = new MqttChannelAdapter(channel, formatterAdapter, MqttNetNullLogger.Instance);
|
||||
return adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ namespace MQTTnet.Tests
|
||||
writer.WriteString("1234567890");
|
||||
Assert.AreEqual(10 + 2, writer.Length);
|
||||
|
||||
writer.WriteBinaryData(new byte[300]);
|
||||
writer.WriteBinary(new byte[300]);
|
||||
Assert.AreEqual(300 + 2 + 12, writer.Length);
|
||||
|
||||
writer.WriteBinaryData(new byte[5000]);
|
||||
writer.WriteBinary(new byte[5000]);
|
||||
Assert.AreEqual(5000 + 2 + 300 + 2 + 12, writer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,8 +598,7 @@ namespace MQTTnet.Tests.Server
|
||||
// c
|
||||
var c1 = await testEnvironment.ConnectClient(clientOptionsBuilder);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
await LongTestDelay();
|
||||
var flow = string.Join(string.Empty, events);
|
||||
Assert.AreEqual("c", flow);
|
||||
|
||||
@@ -607,10 +606,8 @@ namespace MQTTnet.Tests.Server
|
||||
// Connect client with same client ID. Should disconnect existing client.
|
||||
var c2 = await testEnvironment.ConnectClient(clientOptionsBuilder);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
await LongTestDelay();
|
||||
flow = string.Join(string.Empty, events);
|
||||
|
||||
Assert.AreEqual("cdc", flow);
|
||||
|
||||
c2.ApplicationMessageReceivedAsync += e =>
|
||||
@@ -628,8 +625,7 @@ namespace MQTTnet.Tests.Server
|
||||
// r
|
||||
await c2.PublishStringAsync("topic");
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
await LongTestDelay();
|
||||
flow = string.Join(string.Empty, events);
|
||||
Assert.AreEqual("cdcr", flow);
|
||||
|
||||
@@ -639,16 +635,17 @@ namespace MQTTnet.Tests.Server
|
||||
await c1.TryDisconnectAsync();
|
||||
Assert.AreEqual(false, c1.IsConnected);
|
||||
|
||||
await Task.Delay(500);
|
||||
await LongTestDelay();
|
||||
|
||||
// d
|
||||
Assert.AreEqual(true, c2.IsConnected);
|
||||
await c2.DisconnectAsync();
|
||||
|
||||
await Task.Delay(500);
|
||||
await LongTestDelay();
|
||||
|
||||
await server.StopAsync();
|
||||
|
||||
await LongTestDelay();
|
||||
flow = string.Join(string.Empty, events);
|
||||
Assert.AreEqual("cdcrd", flow);
|
||||
}
|
||||
@@ -852,11 +849,11 @@ namespace MQTTnet.Tests.Server
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await Task.Delay(100);
|
||||
|
||||
// Stopping the server should disconnect the connection with the client
|
||||
// which, it turn, will fire the disconnected event at the client.
|
||||
await server.StopAsync();
|
||||
|
||||
await Task.Delay(100);
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, disconnectCalled);
|
||||
}
|
||||
@@ -954,7 +951,7 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
client.InspectPackage += e =>
|
||||
client.InspectPacketAsync += e =>
|
||||
{
|
||||
if (e.Buffer.Length > 0)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,445 @@
|
||||
#if !(NET452 || NET461)
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Certificates;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
// missing certificate builder api means tests won't work for older frameworks
|
||||
|
||||
[TestClass]
|
||||
public sealed class HotSwapCerts_Tests
|
||||
{
|
||||
static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
[TestMethod]
|
||||
public async Task ClientCertChangeWithoutServerUpdateFailsReconnect()
|
||||
{
|
||||
using (var server = new ServerTestHarness())
|
||||
using (var client01 = new ClientTestHarness())
|
||||
{
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.HotSwapClientCert();
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.WaitForConnectToFail(DefaultTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ClientCertChangeWithServerUpdateAcceptsReconnect()
|
||||
{
|
||||
using (var server = new ServerTestHarness())
|
||||
using (var client01 = new ClientTestHarness())
|
||||
{
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.HotSwapClientCert();
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ServerCertChangeWithClientCertUpdateAllowsReconnect()
|
||||
{
|
||||
using (var server = new ServerTestHarness())
|
||||
using (var client01 = new ClientTestHarness())
|
||||
{
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
server.HotSwapServerCert();
|
||||
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ServerCertChangeWithoutClientCertUpdateFailsReconnect()
|
||||
{
|
||||
using (var server = new ServerTestHarness())
|
||||
using (var client01 = new ClientTestHarness())
|
||||
{
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
server.HotSwapServerCert();
|
||||
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.WaitForConnectToFail(DefaultTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
static X509Certificate2 CreateSelfSignedCertificate(string oid)
|
||||
{
|
||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
||||
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
||||
sanBuilder.AddDnsName("localhost");
|
||||
|
||||
using (var rsa = RSA.Create())
|
||||
{
|
||||
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||
|
||||
certRequest.CertificateExtensions.Add(
|
||||
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||
|
||||
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
|
||||
{
|
||||
var pfxCertificate = new X509Certificate2(
|
||||
certificate.Export(X509ContentType.Pfx),
|
||||
(string)null,
|
||||
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
||||
|
||||
return pfxCertificate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ClientTestHarness : IDisposable
|
||||
{
|
||||
readonly HotSwappableClientCertProvider _hotSwapClient = new HotSwappableClientCertProvider();
|
||||
IMqttClient _client;
|
||||
|
||||
public string ClientId => _client.Options.ClientId;
|
||||
|
||||
public void ClearServerCerts()
|
||||
{
|
||||
_hotSwapClient.ClearServerCerts();
|
||||
}
|
||||
|
||||
public Task Connect()
|
||||
{
|
||||
return Run_Client_Connection();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client.Dispose();
|
||||
}
|
||||
|
||||
public X509Certificate2 GetCurrentClientCert()
|
||||
{
|
||||
var result = _hotSwapClient.GetCertificates()[0];
|
||||
return new X509Certificate2(result);
|
||||
}
|
||||
|
||||
public void HotSwapClientCert()
|
||||
{
|
||||
_hotSwapClient.HotSwapCert();
|
||||
}
|
||||
|
||||
public void InstallNewServerCert(X509Certificate2 serverCert)
|
||||
{
|
||||
_hotSwapClient.InstallNewServerCert(serverCert);
|
||||
}
|
||||
|
||||
public void WaitForConnectOrFail(TimeSpan timeout)
|
||||
{
|
||||
if (!_client.IsConnected)
|
||||
{
|
||||
_client.ReconnectAsync().Wait(timeout);
|
||||
}
|
||||
|
||||
WaitForConnect(timeout);
|
||||
|
||||
Assert.IsNotNull(_client, "Client was never initialized");
|
||||
Assert.IsTrue(_client.IsConnected, $"Client connection failed after {timeout}");
|
||||
}
|
||||
|
||||
public void WaitForConnectToFail(TimeSpan timeout)
|
||||
{
|
||||
Assert.IsFalse(_client.IsConnected, "Client should be disconnected before waiting for connect.");
|
||||
|
||||
WaitForConnect(timeout);
|
||||
|
||||
Assert.IsNotNull(_client, "Client was never initialized");
|
||||
Assert.IsFalse(_client.IsConnected, "Client connection success but test wanted fail");
|
||||
}
|
||||
|
||||
public void WaitForDisconnect(TimeSpan timeout)
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
while ((_client == null || _client.IsConnected) && timer.Elapsed < timeout)
|
||||
{
|
||||
Thread.Sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
public void WaitForDisconnectOrFail(TimeSpan timeout)
|
||||
{
|
||||
WaitForDisconnect(timeout);
|
||||
|
||||
Assert.IsNotNull(_client, "Client was never initialized");
|
||||
Assert.IsFalse(_client.IsConnected, $"Client connection should have disconnected after {timeout}");
|
||||
}
|
||||
|
||||
async Task Run_Client_Connection()
|
||||
{
|
||||
var optionsBuilder = new MqttClientOptionsBuilder()
|
||||
.WithTlsOptions(
|
||||
o => o.WithClientCertificatesProvider(_hotSwapClient)
|
||||
.WithCertificateValidationHandler(_hotSwapClient.OnCertificateValidation)
|
||||
.WithSslProtocols(SslProtocols.Tls12))
|
||||
.WithTcpServer("localhost")
|
||||
.WithCleanSession()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500);
|
||||
|
||||
var mqttClientOptions = optionsBuilder.Build();
|
||||
|
||||
var factory = new MqttFactory();
|
||||
var mqttClient = factory.CreateMqttClient();
|
||||
_client = mqttClient;
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions);
|
||||
}
|
||||
|
||||
void WaitForConnect(TimeSpan timeout)
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
while ((_client == null || !_client.IsConnected) && timer.Elapsed < timeout)
|
||||
{
|
||||
Thread.Sleep(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ServerTestHarness : IDisposable
|
||||
{
|
||||
readonly HotSwappableServerCertProvider _hotSwapServer = new HotSwappableServerCertProvider();
|
||||
|
||||
MqttServer _server;
|
||||
|
||||
public void ClearClientCerts()
|
||||
{
|
||||
_hotSwapServer.ClearClientCerts();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_server != null)
|
||||
{
|
||||
_server.StopAsync().Wait();
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
if (_hotSwapServer != null)
|
||||
{
|
||||
_hotSwapServer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ForceDisconnectAsync(ClientTestHarness client)
|
||||
{
|
||||
await _server.DisconnectClientAsync(client.ClientId, MqttDisconnectReasonCode.UnspecifiedError);
|
||||
}
|
||||
|
||||
public X509Certificate2 GetCurrentServerCert()
|
||||
{
|
||||
return _hotSwapServer.GetCertificate();
|
||||
}
|
||||
|
||||
public void HotSwapServerCert()
|
||||
{
|
||||
_hotSwapServer.HotSwapCert();
|
||||
}
|
||||
|
||||
public void InstallNewClientCert(X509Certificate2 serverCert)
|
||||
{
|
||||
_hotSwapServer.InstallNewClientCert(serverCert);
|
||||
}
|
||||
|
||||
public async Task StartServer()
|
||||
{
|
||||
var mqttFactory = new MqttFactory();
|
||||
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(_hotSwapServer)
|
||||
.WithRemoteCertificateValidationCallback(_hotSwapServer.RemoteCertificateValidationCallback)
|
||||
.WithEncryptedEndpoint()
|
||||
.Build();
|
||||
|
||||
mqttServerOptions.TlsEndpointOptions.ClientCertificateRequired = true;
|
||||
_server = mqttFactory.CreateMqttServer(mqttServerOptions);
|
||||
await _server.StartAsync();
|
||||
}
|
||||
}
|
||||
|
||||
class HotSwappableClientCertProvider : IMqttClientCertificatesProvider
|
||||
{
|
||||
X509Certificate2Collection _certificates;
|
||||
ConcurrentBag<X509Certificate2> ServerCerts = new ConcurrentBag<X509Certificate2>();
|
||||
|
||||
public HotSwappableClientCertProvider()
|
||||
{
|
||||
_certificates = new X509Certificate2Collection(CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.2"));
|
||||
}
|
||||
|
||||
public void ClearServerCerts()
|
||||
{
|
||||
ServerCerts = new ConcurrentBag<X509Certificate2>();
|
||||
}
|
||||
|
||||
public X509CertificateCollection GetCertificates()
|
||||
{
|
||||
return new X509Certificate2Collection(_certificates);
|
||||
}
|
||||
|
||||
public void HotSwapCert()
|
||||
{
|
||||
var newCert = new X509Certificate2Collection(CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.2"));
|
||||
var oldCerts = Interlocked.Exchange(ref _certificates, newCert);
|
||||
}
|
||||
|
||||
public void InstallNewServerCert(X509Certificate2 serverCert)
|
||||
{
|
||||
ServerCerts.Add(serverCert);
|
||||
}
|
||||
|
||||
public bool OnCertificateValidation(MqttClientCertificateValidationEventArgs certContext)
|
||||
{
|
||||
var serverCerts = ServerCerts.ToArray();
|
||||
|
||||
var providedCert = certContext.Certificate.GetRawCertData();
|
||||
for (int i = 0, n = serverCerts.Length; i < n; i++)
|
||||
{
|
||||
var currentcert = serverCerts[i];
|
||||
|
||||
if (currentcert.RawData.SequenceEqual(providedCert))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Dispose()
|
||||
{
|
||||
if (_certificates != null)
|
||||
{
|
||||
foreach (var certs in _certificates)
|
||||
{
|
||||
#if !NET452
|
||||
certs.Dispose();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HotSwappableServerCertProvider : ICertificateProvider, IDisposable
|
||||
{
|
||||
X509Certificate2 _certificate;
|
||||
ConcurrentBag<X509Certificate2> ClientCerts = new ConcurrentBag<X509Certificate2>();
|
||||
|
||||
public HotSwappableServerCertProvider()
|
||||
{
|
||||
_certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
|
||||
}
|
||||
|
||||
public void ClearClientCerts()
|
||||
{
|
||||
ClientCerts = new ConcurrentBag<X509Certificate2>();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
#if !NET452
|
||||
_certificate.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
public X509Certificate2 GetCertificate()
|
||||
{
|
||||
return _certificate;
|
||||
}
|
||||
|
||||
public void HotSwapCert()
|
||||
{
|
||||
var newCert = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
|
||||
var oldCert = Interlocked.Exchange(ref _certificate, newCert);
|
||||
#if !NET452
|
||||
oldCert.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void InstallNewClientCert(X509Certificate2 certificate)
|
||||
{
|
||||
ClientCerts.Add(certificate);
|
||||
}
|
||||
|
||||
public bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
var serverCerts = ClientCerts.ToArray();
|
||||
|
||||
var providedCert = certificate.GetRawCertData();
|
||||
for (int i = 0, n = serverCerts.Length; i < n; i++)
|
||||
{
|
||||
var currentCert = serverCerts[i];
|
||||
|
||||
if (currentCert.RawData.SequenceEqual(providedCert))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttRetainedMessageManager_Tests
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task MqttRetainedMessageManager_GetUndefinedTopic()
|
||||
{
|
||||
var logger = new Mockups.TestLogger();
|
||||
var eventContainer = new MQTTnet.Server.MqttServerEventContainer();
|
||||
var retainedMessagesManager = new MQTTnet.Server.MqttRetainedMessagesManager(eventContainer, logger);
|
||||
var task = retainedMessagesManager.GetMessage("undefined");
|
||||
Assert.IsNotNull(task, "Task should not be null");
|
||||
var result = await task;
|
||||
Assert.IsNull(result, "Null result expected");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,14 +221,17 @@ namespace MQTTnet.Tests.Server
|
||||
var eventContainer = new MqttServerEventContainer();
|
||||
var clientSessionManager = new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger);
|
||||
|
||||
var session = new MqttSession("", false, new ConcurrentDictionary<object, object>(), options, eventContainer, retainedMessagesManager, clientSessionManager);
|
||||
var session = new MqttSession(new MqttConnectPacket
|
||||
{
|
||||
ClientId = ""
|
||||
}, new ConcurrentDictionary<object, object>(), options, eventContainer, retainedMessagesManager, clientSessionManager);
|
||||
|
||||
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerEventContainer(), retainedMessagesManager, clientSessionManager);
|
||||
}
|
||||
|
||||
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
|
||||
{
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out _, out _);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out _, out _);
|
||||
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
@@ -27,5 +29,118 @@ namespace MQTTnet.Tests.Server
|
||||
await client.PublishStringAsync("test", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
[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 Intercept_Client_Enqueue()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
await receiver.SubscribeAsync("A");
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
|
||||
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
receivedMessages.AssertReceivedCountEquals(1);
|
||||
|
||||
server.InterceptingClientEnqueueAsync += e =>
|
||||
{
|
||||
e.AcceptEnqueue = false;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
// Do not increase because the internal enqueue to the target client is not accepted!
|
||||
receivedMessages.AssertReceivedCountEquals(1);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Client_Enqueue_Multiple_Clients_Subscribed_Messages_Are_Filtered()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
|
||||
var receiverOne = await testEnvironment.ConnectClient(o => o.WithClientId("One"));
|
||||
await receiverOne.SubscribeAsync("A");
|
||||
var receivedMessagesOne = testEnvironment.CreateApplicationMessageHandler(receiverOne);
|
||||
|
||||
var receiverTwo = await testEnvironment.ConnectClient(o => o.WithClientId("Two"));
|
||||
await receiverTwo.SubscribeAsync("A");
|
||||
var receivedMessagesTwo = testEnvironment.CreateApplicationMessageHandler(receiverTwo);
|
||||
|
||||
var receiverThree = await testEnvironment.ConnectClient(o => o.WithClientId("Three"));
|
||||
await receiverThree.SubscribeAsync("A");
|
||||
var receivedMessagesThree = testEnvironment.CreateApplicationMessageHandler(receiverThree);
|
||||
|
||||
server.InterceptingClientEnqueueAsync += e =>
|
||||
{
|
||||
if (e.ReceiverClientId.Contains("Two")) e.AcceptEnqueue = false;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
receivedMessagesOne.AssertReceivedCountEquals(1);
|
||||
receivedMessagesTwo.AssertReceivedCountEquals(0);
|
||||
receivedMessagesThree.AssertReceivedCountEquals(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
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));
|
||||
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(() => client.ConnectAsync(clientOptions));
|
||||
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, ex.Result.ResultCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,28 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Do_Not_Use_Expired_Session()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(o => o.WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3));
|
||||
|
||||
// Kill the client connection and ensure that the session will stay there but is expired after 3 seconds!
|
||||
c1.Dispose();
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(6));
|
||||
|
||||
c1 = testEnvironment.CreateClient();
|
||||
var options = testEnvironment.CreateDefaultClientOptionsBuilder().WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3).Build();
|
||||
|
||||
var connectResult = await c1.ConnectAsync(options);
|
||||
Assert.AreEqual(false, connectResult.IsSessionPresent);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Deleted_Event()
|
||||
{
|
||||
@@ -121,7 +143,7 @@ namespace MQTTnet.Tests.Server
|
||||
Assert.AreEqual(true, session.Items["can_subscribe_x"]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(MqttProtocolVersion.V310)]
|
||||
[DataRow(MqttProtocolVersion.V311)]
|
||||
@@ -134,16 +156,12 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(1)).WithProtocolVersion(protocolVersion);
|
||||
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(10)).WithProtocolVersion(protocolVersion);
|
||||
|
||||
var hasReceive = false;
|
||||
|
||||
void OnReceive()
|
||||
{
|
||||
if (!hasReceive)
|
||||
{
|
||||
hasReceive = true;
|
||||
}
|
||||
hasReceive = true;
|
||||
}
|
||||
|
||||
// Try to connect 50 clients at the same time.
|
||||
@@ -152,7 +170,7 @@ namespace MQTTnet.Tests.Server
|
||||
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
|
||||
Assert.AreEqual(1, connectedClients.Count);
|
||||
|
||||
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
|
||||
@@ -164,7 +182,7 @@ namespace MQTTnet.Tests.Server
|
||||
Assert.AreEqual(true, hasReceive);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
|
||||
@@ -355,8 +373,8 @@ namespace MQTTnet.Tests.Server
|
||||
onReceive();
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
|
||||
{
|
||||
await client.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public async Task Deny_Invalid_Topic()
|
||||
{
|
||||
@@ -90,6 +90,29 @@ namespace MQTTnet.Tests.Server
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Subscribe_With_User_Properties()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
InterceptingSubscriptionEventArgs eventArgs = null;
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var subscribeOptions = testEnvironment.Factory.CreateSubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
|
||||
await client.SubscribeAsync(subscribeOptions);
|
||||
|
||||
CollectionAssert.AreEqual(subscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||
public async Task Disconnect_While_Subscribing()
|
||||
@@ -105,7 +128,7 @@ namespace MQTTnet.Tests.Server
|
||||
await client.SubscribeAsync("#");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public async Task Enqueue_Message_After_Subscription()
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace MQTTnet.Tests.Server
|
||||
var topics = t.Value;
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
||||
|
||||
bucketDepths.TryGetValue(topicHash, out var currentValue);
|
||||
++currentValue;
|
||||
@@ -96,7 +96,7 @@ namespace MQTTnet.Tests.Server
|
||||
{
|
||||
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/#";
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
}
|
||||
@@ -110,7 +110,7 @@ namespace MQTTnet.Tests.Server
|
||||
{
|
||||
var topic = "asdfasdf/asdfasdf/asdfasdf/asdfasdf/asdfas/dfaf/assfdgsdfgdf/+";
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
}
|
||||
@@ -150,7 +150,7 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
// UInt64 is limited to 8 levels
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
var topic = sb.ToString();
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not present");
|
||||
|
||||
@@ -269,7 +269,7 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
var topic = sb.ToString();
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
@@ -319,7 +319,7 @@ namespace MQTTnet.Tests.Server
|
||||
var l3 = "#";
|
||||
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
@@ -354,7 +354,7 @@ namespace MQTTnet.Tests.Server
|
||||
var l3 = "prop1";
|
||||
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsFalse(topicHasWildcard, "Wildcard detected when not wildcard present");
|
||||
|
||||
@@ -447,7 +447,7 @@ namespace MQTTnet.Tests.Server
|
||||
var l3 = "prop1";
|
||||
var topic = $"{l0}/{l1}/{l2}/{l3}";
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out var topicHashMask, out var topicHasWildcard);
|
||||
|
||||
Assert.IsTrue(topicHasWildcard, "Wildcard not detected");
|
||||
|
||||
@@ -475,7 +475,7 @@ namespace MQTTnet.Tests.Server
|
||||
|
||||
void CheckTopicHash(string topic, ulong expectedHash, ulong expectedHashMask)
|
||||
{
|
||||
MqttSubscription.CalculateTopicHash(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out var hashMask, out var hasWildcard);
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Topic: " + topic);
|
||||
@@ -501,7 +501,7 @@ namespace MQTTnet.Tests.Server
|
||||
{
|
||||
var topicToFind = topicsToFind[countDown];
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topicToFind, out var topicHash, out _, out _);
|
||||
MqttTopicHash.Calculate(topicToFind, out var topicHash, out _, out _);
|
||||
|
||||
clientSession.TryCheckSubscriptions(topicToFind, topicHash, MqttQualityOfServiceLevel.AtMostOnce, "OtherClient", out var result);
|
||||
if (result.IsSubscribed)
|
||||
@@ -557,7 +557,7 @@ namespace MQTTnet.Tests.Server
|
||||
var eventContainer = new MqttServerEventContainer();
|
||||
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
|
||||
var sessionManager = new MqttClientSessionsManager(serverOptions, retainedMessagesManager, eventContainer, logger);
|
||||
_clientSession = new MqttSession(clientId, false, new Dictionary<object, object>(), serverOptions, eventContainer, retainedMessagesManager, sessionManager);
|
||||
_clientSession = new MqttSession(new MqttConnectPacket{ ClientId = clientId }, new Dictionary<object, object>(), serverOptions, eventContainer, retainedMessagesManager, sessionManager);
|
||||
|
||||
List<string> topicsToSubscribe;
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
#if !WINDOWS_UWP && (NET48_OR_GREATER || NET5_0_OR_GREATER)
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Certificates;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Tls_Tests : BaseTestClass
|
||||
{
|
||||
static X509Certificate2 CreateCertificate(string oid)
|
||||
{
|
||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
||||
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
||||
sanBuilder.AddDnsName("localhost");
|
||||
|
||||
using (var rsa = RSA.Create())
|
||||
{
|
||||
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||
|
||||
certRequest.CertificateExtensions.Add(
|
||||
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||
|
||||
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
|
||||
{
|
||||
var pfxCertificate = new X509Certificate2(
|
||||
certificate.Export(X509ContentType.Pfx),
|
||||
(string)null,
|
||||
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
||||
|
||||
return pfxCertificate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Tls_Swap_Test()
|
||||
{
|
||||
var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var serverOptionsBuilder = testEnvironment.Factory.CreateServerOptionsBuilder();
|
||||
|
||||
var firstOid = "1.3.6.1.5.5.7.3.1";
|
||||
var secondOid = "1.3.6.1.5.5.7.3.2";
|
||||
|
||||
var certificateProvider = new CertificateProvider
|
||||
{
|
||||
CurrentCertificate = CreateCertificate(firstOid)
|
||||
};
|
||||
|
||||
serverOptionsBuilder.WithoutDefaultEndpoint().WithEncryptedEndpoint().WithEncryptionSslProtocol(SslProtocols.Tls12).WithEncryptionCertificate(certificateProvider);
|
||||
|
||||
var serverOptions = serverOptionsBuilder.Build();
|
||||
|
||||
var server = testEnvironment.CreateServer(serverOptions);
|
||||
|
||||
var publishedCount = 0;
|
||||
server.InterceptingPublishAsync += args =>
|
||||
{
|
||||
Interlocked.Increment(ref publishedCount);
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await server.StartAsync();
|
||||
|
||||
var firstClient = await ConnectClientAsync(
|
||||
testEnvironment,
|
||||
args =>
|
||||
{
|
||||
Assert.AreEqual(firstOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
|
||||
return true;
|
||||
});
|
||||
|
||||
var firstClientReceivedCount = 0;
|
||||
firstClient.ApplicationMessageReceivedAsync += args =>
|
||||
{
|
||||
Interlocked.Increment(ref firstClientReceivedCount);
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await firstClient.SubscribeAsync("TestTopic1");
|
||||
|
||||
await firstClient.PublishAsync(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic1",
|
||||
Payload = new byte[] { 1, 2, 3, 4 }
|
||||
});
|
||||
|
||||
await testEnvironment.Server.InjectApplicationMessage(
|
||||
new InjectedMqttApplicationMessage(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic1",
|
||||
Payload = new byte[] { 1, 2, 3, 4 }
|
||||
}));
|
||||
|
||||
certificateProvider.CurrentCertificate = CreateCertificate(secondOid);
|
||||
|
||||
// Validate that the certificate was switched
|
||||
var secondClient = await ConnectClientAsync(
|
||||
testEnvironment,
|
||||
args =>
|
||||
{
|
||||
Assert.AreEqual(secondOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
|
||||
return true;
|
||||
});
|
||||
|
||||
var secondClientReceivedCount = 0;
|
||||
secondClient.ApplicationMessageReceivedAsync += args =>
|
||||
{
|
||||
Interlocked.Increment(ref secondClientReceivedCount);
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await secondClient.SubscribeAsync("TestTopic2");
|
||||
|
||||
await firstClient.PublishAsync(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic2",
|
||||
Payload = new byte[] { 1, 2, 3, 4 }
|
||||
});
|
||||
|
||||
await testEnvironment.Server.InjectApplicationMessage(
|
||||
new InjectedMqttApplicationMessage(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic2",
|
||||
Payload = new byte[] { 1, 2, 3, 4 }
|
||||
}));
|
||||
|
||||
// Ensure first client still works
|
||||
await firstClient.PublishAsync(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic1",
|
||||
Payload = new byte[] { 1, 2, 3, 4 }
|
||||
});
|
||||
|
||||
await testEnvironment.Server.InjectApplicationMessage(
|
||||
new InjectedMqttApplicationMessage(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic1",
|
||||
Payload = new byte[] { 1, 2, 3, 4 }
|
||||
}));
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.AreEqual(6, publishedCount);
|
||||
Assert.AreEqual(4, firstClientReceivedCount);
|
||||
Assert.AreEqual(2, secondClientReceivedCount);
|
||||
|
||||
await server.StopAsync();
|
||||
}
|
||||
|
||||
static async Task<IMqttClient> ConnectClientAsync(TestEnvironment testEnvironment, Func<MqttClientCertificateValidationEventArgs, bool> certValidator)
|
||||
{
|
||||
var clientOptionsBuilder = testEnvironment.Factory.CreateClientOptionsBuilder();
|
||||
clientOptionsBuilder.WithClientId(Guid.NewGuid().ToString())
|
||||
.WithTcpServer("localhost", 8883)
|
||||
.WithTls(
|
||||
tls =>
|
||||
{
|
||||
tls.UseTls = true;
|
||||
tls.SslProtocol = SslProtocols.Tls12;
|
||||
tls.CertificateValidationHandler = certValidator;
|
||||
});
|
||||
|
||||
var clientOptions = clientOptionsBuilder.Build();
|
||||
return await testEnvironment.ConnectClient(clientOptions);
|
||||
}
|
||||
|
||||
sealed class CertificateProvider : ICertificateProvider
|
||||
{
|
||||
public X509Certificate2 CurrentCertificate { get; set; }
|
||||
|
||||
public X509Certificate2 GetCertificate()
|
||||
{
|
||||
return CurrentCertificate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -2,10 +2,14 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
{
|
||||
@@ -28,5 +32,28 @@ namespace MQTTnet.Tests.Server
|
||||
await client.UnsubscribeAsync("#");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Unsubscribe_With_User_Properties()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
InterceptingUnsubscriptionEventArgs eventArgs = null;
|
||||
server.InterceptingUnsubscriptionAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var unsubscribeOptions = testEnvironment.Factory.CreateUnsubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
|
||||
await client.UnsubscribeAsync(unsubscribeOptions);
|
||||
|
||||
CollectionAssert.AreEqual(unsubscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,18 +150,8 @@ namespace MQTTnet.Tests
|
||||
{
|
||||
Assert.AreEqual(expectedResult, MqttTopicFilterComparer.Compare(topic, filter));
|
||||
|
||||
ulong topicHash;
|
||||
ulong topicHashMask;
|
||||
bool topicHasWildcard;
|
||||
|
||||
MqttSubscription.CalculateTopicHash(topic, out topicHash, out topicHashMask, out topicHasWildcard);
|
||||
|
||||
|
||||
ulong filterTopicHash;
|
||||
ulong filterTopicHashMask;
|
||||
bool filterTopicHasWildcard;
|
||||
|
||||
MqttSubscription.CalculateTopicHash(filter, out filterTopicHash, out filterTopicHashMask, out filterTopicHasWildcard);
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out _, out _);
|
||||
MqttTopicHash.Calculate(filter, out var filterTopicHash, out var filterTopicHashMask, out _);
|
||||
|
||||
if (expectedResult == MqttTopicFilterCompareResult.IsMatch)
|
||||
{
|
||||
|
||||
@@ -27,18 +27,15 @@ namespace MQTTnet.Adapter
|
||||
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
|
||||
readonly MqttPacketInspector _packetInspector;
|
||||
|
||||
readonly byte[] _singleByteBuffer = new byte[1];
|
||||
|
||||
readonly AsyncLock _syncRoot = new AsyncLock();
|
||||
|
||||
Statistics _statistics; // mutable struct, don't make readonly!
|
||||
Statistics _statistics; // mutable struct, don't make readonly!
|
||||
|
||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, MqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttNetLogger logger)
|
||||
{
|
||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
||||
_packetInspector = packetInspector;
|
||||
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
|
||||
@@ -64,6 +61,10 @@ namespace MQTTnet.Adapter
|
||||
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
public MqttPacketInspector PacketInspector { get; set; }
|
||||
|
||||
public bool AllowPacketFragmentation { get; set; } = true;
|
||||
|
||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -126,7 +127,7 @@ namespace MQTTnet.Adapter
|
||||
|
||||
try
|
||||
{
|
||||
_packetInspector?.BeginReceivePacket();
|
||||
PacketInspector?.BeginReceivePacket();
|
||||
|
||||
ReceivedMqttPacket receivedPacket;
|
||||
var receivedPacketTask = ReceiveAsync(cancellationToken);
|
||||
@@ -144,7 +145,7 @@ namespace MQTTnet.Adapter
|
||||
return null;
|
||||
}
|
||||
|
||||
_packetInspector?.EndReceivePacket();
|
||||
PacketInspector?.EndReceivePacket();
|
||||
|
||||
Interlocked.Add(ref _statistics._bytesSent, receivedPacket.TotalLength);
|
||||
|
||||
@@ -180,7 +181,10 @@ namespace MQTTnet.Adapter
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ResetStatistics() => _statistics.Reset();
|
||||
public void ResetStatistics()
|
||||
{
|
||||
_statistics.Reset();
|
||||
}
|
||||
|
||||
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -197,18 +201,18 @@ namespace MQTTnet.Adapter
|
||||
try
|
||||
{
|
||||
var packetBuffer = PacketFormatterAdapter.Encode(packet);
|
||||
_packetInspector?.BeginSendPacket(packetBuffer);
|
||||
PacketInspector?.BeginSendPacket(packetBuffer);
|
||||
|
||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetBuffer.Length, packet);
|
||||
|
||||
if (packetBuffer.Payload.Count > 0)
|
||||
|
||||
if (packetBuffer.Payload.Count == 0 || !AllowPacketFragmentation)
|
||||
{
|
||||
await _channel.WriteAsync(packetBuffer.Packet, false, cancellationToken).ConfigureAwait(false);
|
||||
await _channel.WriteAsync(packetBuffer.Payload, true, cancellationToken).ConfigureAwait(false);
|
||||
await _channel.WriteAsync(packetBuffer.Join(), true, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _channel.WriteAsync(packetBuffer.Packet, true, cancellationToken).ConfigureAwait(false);
|
||||
await _channel.WriteAsync(packetBuffer.Packet, false, cancellationToken).ConfigureAwait(false);
|
||||
await _channel.WriteAsync(packetBuffer.Payload, true, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Interlocked.Add(ref _statistics._bytesReceived, packetBuffer.Length);
|
||||
@@ -270,7 +274,7 @@ namespace MQTTnet.Adapter
|
||||
return 0;
|
||||
}
|
||||
|
||||
_packetInspector?.FillReceiveBuffer(_singleByteBuffer);
|
||||
PacketInspector?.FillReceiveBuffer(_singleByteBuffer);
|
||||
|
||||
encodedByte = _singleByteBuffer[0];
|
||||
|
||||
@@ -309,7 +313,7 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
return ReadFixedHeaderResult.ConnectionClosed;
|
||||
}
|
||||
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return ReadFixedHeaderResult.Canceled;
|
||||
@@ -323,7 +327,7 @@ namespace MQTTnet.Adapter
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
|
||||
_packetInspector?.FillReceiveBuffer(buffer);
|
||||
PacketInspector?.FillReceiveBuffer(buffer);
|
||||
|
||||
var hasRemainingLength = buffer[1] != 0;
|
||||
if (!hasRemainingLength)
|
||||
@@ -407,7 +411,7 @@ namespace MQTTnet.Adapter
|
||||
bodyOffset += readBytes;
|
||||
} while (bodyOffset < bodyLength);
|
||||
|
||||
_packetInspector?.FillReceiveBuffer(body);
|
||||
PacketInspector?.FillReceiveBuffer(body);
|
||||
|
||||
var bodySegment = new ArraySegment<byte>(body, 0, bodyLength);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, bodySegment, fixedHeader.TotalLength);
|
||||
@@ -455,7 +459,7 @@ namespace MQTTnet.Adapter
|
||||
throw new MqttCommunicationException(exception);
|
||||
}
|
||||
|
||||
private struct Statistics
|
||||
struct Statistics
|
||||
{
|
||||
public long _bytesReceived;
|
||||
public long _bytesSent;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace MQTTnet.Client
|
||||
|
||||
event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync;
|
||||
|
||||
event Func<InspectMqttPacketEventArgs, Task> InspectPackage;
|
||||
event Func<InspectMqttPacketEventArgs, Task> InspectPacketAsync;
|
||||
|
||||
bool IsConnected { get; }
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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 MQTTnet.Diagnostics;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Client.Internal
|
||||
{
|
||||
public sealed class MqttClientEvents
|
||||
{
|
||||
public AsyncEvent<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceivedEvent { get; } = new AsyncEvent<MqttApplicationMessageReceivedEventArgs>();
|
||||
public AsyncEvent<MqttClientConnectedEventArgs> ConnectedEvent { get; } = new AsyncEvent<MqttClientConnectedEventArgs>();
|
||||
public AsyncEvent<MqttClientConnectingEventArgs> ConnectingEvent { get; } = new AsyncEvent<MqttClientConnectingEventArgs>();
|
||||
public AsyncEvent<MqttClientDisconnectedEventArgs> DisconnectedEvent { get; } = new AsyncEvent<MqttClientDisconnectedEventArgs>();
|
||||
public AsyncEvent<InspectMqttPacketEventArgs> InspectPacketEvent { get; } = new AsyncEvent<InspectMqttPacketEventArgs>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// 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.
|
||||
|
||||
namespace MQTTnet.Client.Internal
|
||||
{
|
||||
public static class MqttClientResultFactory
|
||||
{
|
||||
public static readonly MqttClientConnectResultFactory ConnectResult = new MqttClientConnectResultFactory();
|
||||
public static readonly MqttClientPublishResultFactory PublishResult = new MqttClientPublishResultFactory();
|
||||
public static readonly MqttClientSubscribeResultFactory SubscribeResult = new MqttClientSubscribeResultFactory();
|
||||
public static readonly MqttClientUnsubscribeResultFactory UnsubscribeResult = new MqttClientUnsubscribeResultFactory();
|
||||
}
|
||||
}
|
||||
+216
-165
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client.Internal;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
@@ -20,17 +21,9 @@ namespace MQTTnet.Client
|
||||
public sealed class MqttClient : Disposable, IMqttClient
|
||||
{
|
||||
readonly IMqttClientAdapterFactory _adapterFactory;
|
||||
readonly AsyncEvent<MqttApplicationMessageReceivedEventArgs> _applicationMessageReceivedEvent = new AsyncEvent<MqttApplicationMessageReceivedEventArgs>();
|
||||
|
||||
readonly MqttClientPublishResultFactory _clientPublishResultFactory = new MqttClientPublishResultFactory();
|
||||
readonly MqttClientSubscribeResultFactory _clientSubscribeResultFactory = new MqttClientSubscribeResultFactory();
|
||||
readonly MqttClientUnsubscribeResultFactory _clientUnsubscribeResultFactory = new MqttClientUnsubscribeResultFactory();
|
||||
|
||||
readonly AsyncEvent<MqttClientConnectedEventArgs> _connectedEvent = new AsyncEvent<MqttClientConnectedEventArgs>();
|
||||
readonly AsyncEvent<MqttClientConnectingEventArgs> _connectingEvent = new AsyncEvent<MqttClientConnectingEventArgs>();
|
||||
readonly AsyncEvent<MqttClientDisconnectedEventArgs> _disconnectedEvent = new AsyncEvent<MqttClientDisconnectedEventArgs>();
|
||||
readonly object _disconnectLock = new object();
|
||||
readonly AsyncEvent<InspectMqttPacketEventArgs> _inspectPacketEvent = new AsyncEvent<InspectMqttPacketEventArgs>();
|
||||
readonly MqttClientEvents _events = new MqttClientEvents();
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
|
||||
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
|
||||
@@ -65,32 +58,32 @@ namespace MQTTnet.Client
|
||||
|
||||
public event Func<MqttApplicationMessageReceivedEventArgs, Task> ApplicationMessageReceivedAsync
|
||||
{
|
||||
add => _applicationMessageReceivedEvent.AddHandler(value);
|
||||
remove => _applicationMessageReceivedEvent.RemoveHandler(value);
|
||||
add => _events.ApplicationMessageReceivedEvent.AddHandler(value);
|
||||
remove => _events.ApplicationMessageReceivedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttClientConnectedEventArgs, Task> ConnectedAsync
|
||||
{
|
||||
add => _connectedEvent.AddHandler(value);
|
||||
remove => _connectedEvent.RemoveHandler(value);
|
||||
add => _events.ConnectedEvent.AddHandler(value);
|
||||
remove => _events.ConnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttClientConnectingEventArgs, Task> ConnectingAsync
|
||||
{
|
||||
add => _connectingEvent.AddHandler(value);
|
||||
remove => _connectingEvent.RemoveHandler(value);
|
||||
add => _events.ConnectingEvent.AddHandler(value);
|
||||
remove => _events.ConnectingEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync
|
||||
{
|
||||
add => _disconnectedEvent.AddHandler(value);
|
||||
remove => _disconnectedEvent.RemoveHandler(value);
|
||||
add => _events.DisconnectedEvent.AddHandler(value);
|
||||
remove => _events.DisconnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InspectMqttPacketEventArgs, Task> InspectPackage
|
||||
public event Func<InspectMqttPacketEventArgs, Task> InspectPacketAsync
|
||||
{
|
||||
add => _inspectPacketEvent.AddHandler(value);
|
||||
remove => _inspectPacketEvent.RemoveHandler(value);
|
||||
add => _events.InspectPacketEvent.AddHandler(value);
|
||||
remove => _events.InspectPacketEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public bool IsConnected => (MqttClientConnectionStatus)_connectionStatus == MqttClientConnectionStatus.Connected;
|
||||
@@ -114,9 +107,9 @@ namespace MQTTnet.Client
|
||||
{
|
||||
Options = options;
|
||||
|
||||
if (_connectingEvent.HasHandlers)
|
||||
if (_events.ConnectingEvent.HasHandlers)
|
||||
{
|
||||
await _connectingEvent.InvokeAsync(new MqttClientConnectingEventArgs(options));
|
||||
await _events.ConnectingEvent.InvokeAsync(new MqttClientConnectingEventArgs(options));
|
||||
}
|
||||
|
||||
Cleanup();
|
||||
@@ -127,12 +120,12 @@ namespace MQTTnet.Client
|
||||
_mqttClientAlive = new CancellationTokenSource();
|
||||
var mqttClientAliveToken = _mqttClientAlive.Token;
|
||||
|
||||
var adapter = _adapterFactory.CreateClientAdapter(options, new MqttPacketInspector(_inspectPacketEvent, _rootLogger), _rootLogger);
|
||||
_adapter = adapter;
|
||||
var adapter = _adapterFactory.CreateClientAdapter(options, new MqttPacketInspector(_events.InspectPacketEvent, _rootLogger), _rootLogger);
|
||||
_adapter = adapter ?? throw new InvalidOperationException("The adapter factory did not provide an adapter.");
|
||||
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
connectResult = await ConnectInternal(cancellationToken).ConfigureAwait(false);
|
||||
connectResult = await ConnectInternal(adapter, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -140,43 +133,51 @@ namespace MQTTnet.Client
|
||||
// CancellationToken.None or similar.
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
connectResult = await ConnectInternal(timeout.Token).ConfigureAwait(false);
|
||||
connectResult = await ConnectInternal(adapter, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (connectResult.ResultCode != MqttClientConnectResultCode.Success)
|
||||
{
|
||||
_logger.Warning("Connecting failed: {0}", connectResult.ResultCode);
|
||||
await DisconnectInternal(null, null, connectResult).ConfigureAwait(false);
|
||||
return connectResult;
|
||||
}
|
||||
|
||||
_lastPacketSentTimestamp = DateTime.UtcNow;
|
||||
|
||||
var keepAliveInterval = Options.KeepAlivePeriod;
|
||||
if (connectResult.ServerKeepAlive > 0)
|
||||
{
|
||||
_logger.Info($"Using keep alive value ({connectResult.ServerKeepAlive}) sent from the server.");
|
||||
_logger.Info($"Using keep alive value ({connectResult.ServerKeepAlive}) sent from the server");
|
||||
keepAliveInterval = TimeSpan.FromSeconds(connectResult.ServerKeepAlive);
|
||||
}
|
||||
|
||||
if (keepAliveInterval != TimeSpan.Zero)
|
||||
{
|
||||
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(mqttClientAliveToken), mqttClientAliveToken);
|
||||
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessages(mqttClientAliveToken), mqttClientAliveToken);
|
||||
}
|
||||
|
||||
CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connected, MqttClientConnectionStatus.Connecting);
|
||||
|
||||
_logger.Info("Connected.");
|
||||
_logger.Info("Connected");
|
||||
|
||||
if (_connectedEvent.HasHandlers)
|
||||
{
|
||||
var eventArgs = new MqttClientConnectedEventArgs(connectResult);
|
||||
await _connectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
await OnConnected(connectResult).ConfigureAwait(false);
|
||||
|
||||
return connectResult;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is MqttConnectingFailedException connectingFailedException)
|
||||
{
|
||||
connectResult = connectingFailedException.Result;
|
||||
}
|
||||
|
||||
_disconnectReason = (int)MqttClientDisconnectOptionsReason.UnspecifiedError;
|
||||
|
||||
_logger.Error(exception, "Error while connecting with server.");
|
||||
_logger.Error(exception, "Error while connecting with server");
|
||||
|
||||
await DisconnectInternalAsync(null, exception, connectResult).ConfigureAwait(false);
|
||||
await DisconnectInternal(null, exception, connectResult).ConfigureAwait(false);
|
||||
|
||||
throw;
|
||||
}
|
||||
@@ -219,33 +220,38 @@ namespace MQTTnet.Client
|
||||
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
|
||||
await Send(disconnectPacket, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
await SendAsync(disconnectPacket, timeout.Token).ConfigureAwait(false);
|
||||
await Send(disconnectPacket, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await DisconnectCoreAsync(null, null, null, clientWasConnected).ConfigureAwait(false);
|
||||
await DisconnectCore(null, null, null, clientWasConnected).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PingAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
await SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken).ConfigureAwait(false);
|
||||
await Request<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
await SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, timeout.Token).ConfigureAwait(false);
|
||||
await Request<MqttPingRespPacket>(MqttPingReqPacket.Instance, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,11 +280,11 @@ namespace MQTTnet.Client
|
||||
}
|
||||
case MqttQualityOfServiceLevel.AtLeastOnce:
|
||||
{
|
||||
return PublishAtLeastOnceAsync(publishPacket, cancellationToken);
|
||||
return PublishAtLeastOnce(publishPacket, cancellationToken);
|
||||
}
|
||||
case MqttQualityOfServiceLevel.ExactlyOnce:
|
||||
{
|
||||
return PublishExactlyOnceAsync(publishPacket, cancellationToken);
|
||||
return PublishExactlyOnce(publishPacket, cancellationToken);
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -306,7 +312,7 @@ namespace MQTTnet.Client
|
||||
UserProperties = data.UserProperties
|
||||
};
|
||||
|
||||
return SendAsync(authPacket, cancellationToken);
|
||||
return Send(authPacket, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken = default)
|
||||
@@ -321,31 +327,31 @@ namespace MQTTnet.Client
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe(topicFilter.Topic);
|
||||
}
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
|
||||
if (Options.ValidateFeatures)
|
||||
{
|
||||
MqttClientSubscribeOptionsValidator.ThrowIfNotSupported(options, _adapter.PacketFormatterAdapter.ProtocolVersion);
|
||||
}
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
|
||||
var subscribePacket = MqttPacketFactories.Subscribe.Create(options);
|
||||
subscribePacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||
|
||||
MqttSubAckPacket subAckPacket;
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, cancellationToken).ConfigureAwait(false);
|
||||
subAckPacket = await Request<MqttSubAckPacket>(subscribePacket, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, timeout.Token).ConfigureAwait(false);
|
||||
subAckPacket = await Request<MqttSubAckPacket>(subscribePacket, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return _clientSubscribeResultFactory.Create(subscribePacket, subAckPacket);
|
||||
return MqttClientResultFactory.SubscribeResult.Create(subscribePacket, subAckPacket);
|
||||
}
|
||||
|
||||
public async Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken = default)
|
||||
@@ -374,17 +380,17 @@ namespace MQTTnet.Client
|
||||
MqttUnsubAckPacket unsubAckPacket;
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, cancellationToken).ConfigureAwait(false);
|
||||
unsubAckPacket = await Request<MqttUnsubAckPacket>(unsubscribePacket, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, timeout.Token).ConfigureAwait(false);
|
||||
unsubAckPacket = await Request<MqttUnsubAckPacket>(unsubscribePacket, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return _clientUnsubscribeResultFactory.Create(unsubscribePacket, unsubAckPacket);
|
||||
return MqttClientResultFactory.UnsubscribeResult.Create(unsubscribePacket, unsubAckPacket);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -408,7 +414,7 @@ namespace MQTTnet.Client
|
||||
if (!eventArgs.ProcessingFailed)
|
||||
{
|
||||
var pubAckPacket = MqttPacketFactories.PubAck.Create(eventArgs);
|
||||
return SendAsync(pubAckPacket, cancellationToken);
|
||||
return Send(pubAckPacket, cancellationToken);
|
||||
}
|
||||
}
|
||||
else if (eventArgs.PublishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
@@ -416,7 +422,7 @@ namespace MQTTnet.Client
|
||||
if (!eventArgs.ProcessingFailed)
|
||||
{
|
||||
var pubRecPacket = MqttPacketFactories.PubRec.Create(eventArgs);
|
||||
return SendAsync(pubRecPacket, cancellationToken);
|
||||
return Send(pubRecPacket, cancellationToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -427,27 +433,50 @@ namespace MQTTnet.Client
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
async Task<MqttClientConnectResult> AuthenticateAsync(MqttClientOptions options, CancellationToken cancellationToken)
|
||||
async Task<MqttClientConnectResult> Authenticate(IMqttChannelAdapter channelAdapter, MqttClientOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
MqttClientConnectResult result;
|
||||
|
||||
try
|
||||
{
|
||||
var connectPacket = MqttPacketFactories.Connect.Create(options);
|
||||
await Send(connectPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
|
||||
var receivedPacket = await Receive(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var clientConnectResultFactory = new MqttClientConnectResultFactory();
|
||||
result = clientConnectResultFactory.Create(connAckPacket, _adapter.PacketFormatterAdapter.ProtocolVersion);
|
||||
if (receivedPacket is MqttConnAckPacket connAckPacket)
|
||||
{
|
||||
result = MqttClientResultFactory.ConnectResult.Create(connAckPacket, channelAdapter.PacketFormatterAdapter.ProtocolVersion);
|
||||
}
|
||||
else if (receivedPacket is MqttAuthPacket)
|
||||
{
|
||||
throw new NotSupportedException("Extended authentication handler is not yet supported");
|
||||
}
|
||||
else if (receivedPacket == null)
|
||||
{
|
||||
throw new MqttCommunicationException("Connection closed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Received an unexpected MQTT packet ({receivedPacket}).");
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new MqttConnectingFailedException($"Error while authenticating. {exception.Message}", exception, null);
|
||||
}
|
||||
|
||||
if (result.ResultCode != MqttClientConnectResultCode.Success)
|
||||
// This is no feature. It is basically a backward compatibility option and should be removed in the future.
|
||||
// The client should not throw any exception if the transport layer connection was successful and the server
|
||||
// did send a proper ACK packet with a non success response.
|
||||
if (options.ThrowOnNonSuccessfulConnectResponse)
|
||||
{
|
||||
throw new MqttConnectingFailedException($"Connecting with MQTT server failed ({result.ResultCode}).", null, result);
|
||||
if (result.ResultCode != MqttClientConnectResultCode.Success)
|
||||
{
|
||||
_logger.Warning(
|
||||
"Client will now throw an _MqttConnectingFailedException_. This is obsolete and will be removed in the future. Consider setting _ThrowOnNonSuccessfulResponseFromServer=False_ in client options.");
|
||||
throw new MqttConnectingFailedException($"Connecting with MQTT server failed ({result.ResultCode}).", null, result);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Verbose("Authenticated MQTT connection with server established.");
|
||||
@@ -482,27 +511,31 @@ namespace MQTTnet.Client
|
||||
return (MqttClientConnectionStatus)Interlocked.CompareExchange(ref _connectionStatus, (int)value, (int)comparand);
|
||||
}
|
||||
|
||||
async Task<MqttClientConnectResult> ConnectInternal(CancellationToken cancellationToken)
|
||||
async Task<MqttClientConnectResult> ConnectInternal(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
||||
{
|
||||
var backgroundCancellationToken = _mqttClientAlive.Token;
|
||||
|
||||
using (var effectiveCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken))
|
||||
{
|
||||
_logger.Verbose("Trying to connect with server '{0}'.", Options.ChannelOptions);
|
||||
await _adapter.ConnectAsync(effectiveCancellationToken.Token).ConfigureAwait(false);
|
||||
_logger.Verbose("Connection with server established.");
|
||||
_logger.Verbose("Trying to connect with server '{0}'", Options.ChannelOptions);
|
||||
await channelAdapter.ConnectAsync(effectiveCancellationToken.Token).ConfigureAwait(false);
|
||||
_logger.Verbose("Connection with server established");
|
||||
|
||||
_publishPacketReceiverQueue?.Dispose();
|
||||
_publishPacketReceiverQueue = new AsyncQueue<MqttPublishPacket>();
|
||||
|
||||
_publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken);
|
||||
_packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken);
|
||||
var connectResult = await Authenticate(channelAdapter, Options, effectiveCancellationToken.Token).ConfigureAwait(false);
|
||||
if (connectResult.ResultCode == MqttClientConnectResultCode.Success)
|
||||
{
|
||||
_publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken);
|
||||
_packetReceiverTask = Task.Run(() => ReceivePacketsLoop(backgroundCancellationToken), backgroundCancellationToken);
|
||||
}
|
||||
|
||||
return await AuthenticateAsync(Options, effectiveCancellationToken.Token).ConfigureAwait(false);
|
||||
return connectResult;
|
||||
}
|
||||
}
|
||||
|
||||
async Task DisconnectCoreAsync(Task sender, Exception exception, MqttClientConnectResult connectResult, bool clientWasConnected)
|
||||
async Task DisconnectCore(Task sender, Exception exception, MqttClientConnectResult connectResult, bool clientWasConnected)
|
||||
{
|
||||
TryInitiateDisconnect();
|
||||
|
||||
@@ -527,7 +560,7 @@ namespace MQTTnet.Client
|
||||
|
||||
try
|
||||
{
|
||||
_packetDispatcher.Dispose(new MqttClientDisconnectedException(exception));
|
||||
_packetDispatcher?.Dispose(new MqttClientDisconnectedException(exception));
|
||||
|
||||
var receiverTask = _packetReceiverTask.WaitAsync(sender, _logger);
|
||||
var publishPacketReceiverTask = _publishPacketReceiverTask.WaitAsync(sender, _logger);
|
||||
@@ -557,17 +590,17 @@ namespace MQTTnet.Client
|
||||
|
||||
// This handler must be executed in a new thread because otherwise a dead lock may happen
|
||||
// when trying to reconnect in that handler etc.
|
||||
Task.Run(() => _disconnectedEvent.InvokeAsync(eventArgs)).RunInBackground(_logger);
|
||||
Task.Run(() => _events.DisconnectedEvent.InvokeAsync(eventArgs)).RunInBackground(_logger);
|
||||
}
|
||||
}
|
||||
|
||||
Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientConnectResult connectResult)
|
||||
Task DisconnectInternal(Task sender, Exception exception, MqttClientConnectResult connectResult)
|
||||
{
|
||||
var clientWasConnected = IsConnected;
|
||||
|
||||
if (!DisconnectIsPendingOrFinished())
|
||||
{
|
||||
return DisconnectCoreAsync(sender, exception, connectResult, clientWasConnected);
|
||||
return DisconnectCore(sender, exception, connectResult, clientWasConnected);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
@@ -612,15 +645,26 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
|
||||
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessage(MqttPublishPacket publishPacket)
|
||||
{
|
||||
var applicationMessage = MqttApplicationMessageFactory.Create(publishPacket);
|
||||
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage, publishPacket, AcknowledgeReceivedPublishPacket);
|
||||
await _applicationMessageReceivedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
await _events.ApplicationMessageReceivedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
|
||||
return eventArgs;
|
||||
}
|
||||
|
||||
Task OnConnected(MqttClientConnectResult connectResult)
|
||||
{
|
||||
if (_events.ConnectedEvent.HasHandlers)
|
||||
{
|
||||
var eventArgs = new MqttClientConnectedEventArgs(connectResult);
|
||||
return _events.ConnectedEvent.InvokeAsync(eventArgs);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket)
|
||||
{
|
||||
var extendedAuthenticationExchangeHandler = Options.ExtendedAuthenticationExchangeHandler;
|
||||
@@ -641,7 +685,7 @@ namespace MQTTnet.Client
|
||||
// Also dispatch disconnect to waiting threads to generate a proper exception.
|
||||
_packetDispatcher.Dispose(new MqttClientUnexpectedDisconnectReceivedException(disconnectPacket));
|
||||
|
||||
return DisconnectInternalAsync(_packetReceiverTask, null, null);
|
||||
return DisconnectInternal(_packetReceiverTask, null, null);
|
||||
}
|
||||
|
||||
async Task ProcessReceivedPublishPackets(CancellationToken cancellationToken)
|
||||
@@ -657,7 +701,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
|
||||
var publishPacket = publishPacketDequeueResult.Item;
|
||||
var eventArgs = await HandleReceivedApplicationMessageAsync(publishPacket).ConfigureAwait(false);
|
||||
var eventArgs = await HandleReceivedApplicationMessage(publishPacket).ConfigureAwait(false);
|
||||
|
||||
if (eventArgs.AutoAcknowledge)
|
||||
{
|
||||
@@ -681,7 +725,7 @@ namespace MQTTnet.Client
|
||||
// The packet is unknown. Probably due to a restart of the client.
|
||||
// So wen send this to the server to trigger a full resend of the message.
|
||||
var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound);
|
||||
return SendAsync(pubRelPacket, cancellationToken);
|
||||
return Send(pubRelPacket, cancellationToken);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
@@ -690,39 +734,115 @@ namespace MQTTnet.Client
|
||||
Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
var pubCompPacket = MqttPacketFactories.PubComp.Create(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||
return SendAsync(pubCompPacket, cancellationToken);
|
||||
return Send(pubCompPacket, cancellationToken);
|
||||
}
|
||||
|
||||
async Task<MqttClientPublishResult> PublishAtLeastOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
async Task<MqttClientPublishResult> PublishAtLeastOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||
|
||||
var pubAckPacket = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
return _clientPublishResultFactory.Create(pubAckPacket);
|
||||
var pubAckPacket = await Request<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
return MqttClientResultFactory.PublishResult.Create(pubAckPacket);
|
||||
}
|
||||
|
||||
async Task<MqttClientPublishResult> PublishAtMostOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
|
||||
await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
await Send(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return _clientPublishResultFactory.Create(null);
|
||||
return MqttClientResultFactory.PublishResult.Create(null);
|
||||
}
|
||||
|
||||
async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
async Task<MqttClientPublishResult> PublishExactlyOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||
|
||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
var pubRecPacket = await Request<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||
|
||||
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
||||
var pubCompPacket = await Request<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return _clientPublishResultFactory.Create(pubRecPacket, pubCompPacket);
|
||||
return MqttClientResultFactory.PublishResult.Create(pubRecPacket, pubCompPacket);
|
||||
}
|
||||
|
||||
async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttPacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttPacket
|
||||
async Task<MqttPacket> Receive(CancellationToken cancellationToken)
|
||||
{
|
||||
var packetTask = _adapter.ReceivePacketAsync(cancellationToken);
|
||||
|
||||
MqttPacket packet;
|
||||
if (packetTask.IsCompleted)
|
||||
{
|
||||
packet = packetTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet = await packetTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
async Task ReceivePacketsLoop(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Verbose("Start receiving packets.");
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var packet = await Receive(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet == null)
|
||||
{
|
||||
await DisconnectInternal(_packetReceiverTask, null, null).ConfigureAwait(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await TryProcessReceivedPacket(packet, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (_cleanDisconnectInitiated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (exception is AggregateException aggregateException)
|
||||
{
|
||||
exception = aggregateException.GetBaseException();
|
||||
}
|
||||
|
||||
if (exception is OperationCanceledException)
|
||||
{
|
||||
}
|
||||
else if (exception is MqttCommunicationException)
|
||||
{
|
||||
_logger.Warning(exception, "Communication error while receiving packets.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Error(exception, "Error while receiving packets.");
|
||||
}
|
||||
|
||||
_packetDispatcher.FailAll(exception);
|
||||
|
||||
await DisconnectInternal(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.Verbose("Stopped receiving packets.");
|
||||
}
|
||||
}
|
||||
|
||||
async Task<TResponsePacket> Request<TResponsePacket>(MqttPacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttPacket
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -736,7 +856,7 @@ namespace MQTTnet.Client
|
||||
{
|
||||
try
|
||||
{
|
||||
await SendAsync(requestPacket, cancellationToken).ConfigureAwait(false);
|
||||
await Send(requestPacket, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -760,7 +880,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
Task SendAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||
Task Send(MqttPacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -823,7 +943,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
async Task TryProcessReceivedPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||
async Task TryProcessReceivedPacket(MqttPacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -884,80 +1004,11 @@ namespace MQTTnet.Client
|
||||
|
||||
_packetDispatcher.FailAll(exception);
|
||||
|
||||
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
||||
await DisconnectInternal(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
async Task TryReceivePacketsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Verbose("Start receiving packets.");
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
MqttPacket packet;
|
||||
var packetTask = _adapter.ReceivePacketAsync(cancellationToken);
|
||||
|
||||
if (packetTask.IsCompleted)
|
||||
{
|
||||
packet = packetTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet = await packetTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet == null)
|
||||
{
|
||||
await DisconnectInternalAsync(_packetReceiverTask, null, null).ConfigureAwait(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await TryProcessReceivedPacketAsync(packet, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (_cleanDisconnectInitiated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (exception is AggregateException aggregateException)
|
||||
{
|
||||
exception = aggregateException.GetBaseException();
|
||||
}
|
||||
|
||||
if (exception is OperationCanceledException)
|
||||
{
|
||||
}
|
||||
else if (exception is MqttCommunicationException)
|
||||
{
|
||||
_logger.Warning(exception, "Communication error while receiving packets.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Error(exception, "Error while receiving packets.");
|
||||
}
|
||||
|
||||
_packetDispatcher.FailAll(exception);
|
||||
|
||||
await DisconnectInternalAsync(_packetReceiverTask, exception, null).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.Verbose("Stopped receiving packets.");
|
||||
}
|
||||
}
|
||||
|
||||
async Task TrySendKeepAliveMessagesAsync(CancellationToken cancellationToken)
|
||||
async Task TrySendKeepAliveMessages(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1006,7 +1057,7 @@ namespace MQTTnet.Client
|
||||
_logger.Error(exception, "Error exception while sending/receiving keep alive packets.");
|
||||
}
|
||||
|
||||
await DisconnectInternalAsync(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
|
||||
await DisconnectInternal(_keepAlivePacketsSenderTask, exception, null).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -1014,4 +1065,4 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class DefaultMqttCertificatesProvider : IMqttClientCertificatesProvider
|
||||
{
|
||||
readonly X509Certificate2Collection _certificates;
|
||||
|
||||
public DefaultMqttCertificatesProvider(X509Certificate2Collection certificates)
|
||||
{
|
||||
_certificates = certificates;
|
||||
}
|
||||
|
||||
public DefaultMqttCertificatesProvider(IEnumerable<X509Certificate> certificates)
|
||||
{
|
||||
if (certificates != null)
|
||||
{
|
||||
_certificates = new X509Certificate2Collection();
|
||||
foreach (var certificate in certificates)
|
||||
{
|
||||
_certificates.Add(certificate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public X509CertificateCollection GetCertificates()
|
||||
{
|
||||
return _certificates;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public interface IMqttClientCertificatesProvider
|
||||
{
|
||||
System.Security.Cryptography.X509Certificates.X509CertificateCollection GetCertificates();
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,14 @@ namespace MQTTnet.Client
|
||||
|
||||
public X509Chain Chain { get; set; }
|
||||
|
||||
public SslPolicyErrors SslPolicyErrors { get; set; }
|
||||
|
||||
public IMqttClientChannelOptions ClientOptions { get; set; }
|
||||
#if NET452 || NET461 || NET48
|
||||
/// <summary>
|
||||
/// Can be a host string name or an object derived from WebRequest.
|
||||
/// </summary>
|
||||
public object Sender { get; set; }
|
||||
#endif
|
||||
|
||||
public SslPolicyErrors SslPolicyErrors { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,14 @@ namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Usually the MQTT packets can be send partially. This is done by using multiple TCP packets
|
||||
/// or WebSocket frames etc. Unfortunately not all brokers (like Amazon Web Services (AWS)) do support this feature and
|
||||
/// will close the connection when receiving such packets. If such a service is used this flag must
|
||||
/// be set to _false_.
|
||||
/// </summary>
|
||||
public bool AllowPacketFragmentation { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the authentication data.
|
||||
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||
@@ -92,6 +100,11 @@ namespace MQTTnet.Client
|
||||
/// </summary>
|
||||
public uint SessionExpiryInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether an exception should be thrown when the server has sent a non success ACK packet.
|
||||
/// </summary>
|
||||
public bool ThrowOnNonSuccessfulConnectResponse { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timeout which will be applied at socket level and internal operations.
|
||||
/// The default value is the same as for sockets in .NET in general.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// 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.
|
||||
|
||||
@@ -15,10 +15,14 @@ namespace MQTTnet.Client
|
||||
public sealed class MqttClientOptionsBuilder
|
||||
{
|
||||
readonly MqttClientOptions _options = new MqttClientOptions();
|
||||
MqttClientWebSocketProxyOptions _proxyOptions;
|
||||
|
||||
[Obsolete] MqttClientWebSocketProxyOptions _proxyOptions;
|
||||
|
||||
MqttClientTcpOptions _tcpOptions;
|
||||
MqttClientOptionsBuilderTlsParameters _tlsParameters;
|
||||
MqttClientTlsOptions _tlsOptions;
|
||||
|
||||
[Obsolete] MqttClientOptionsBuilderTlsParameters _tlsParameters;
|
||||
|
||||
MqttClientWebSocketOptions _webSocketOptions;
|
||||
|
||||
public MqttClientOptions Build()
|
||||
@@ -28,11 +32,17 @@ namespace MQTTnet.Client
|
||||
throw new InvalidOperationException("A channel must be set.");
|
||||
}
|
||||
|
||||
// The user can specify the TCP options with already configured TLS options
|
||||
// or start with TLS settings not knowing which transport will be used (depending
|
||||
// on the order of called methods from the builder).
|
||||
// The builder prefers the explicitly set TLS options!
|
||||
var tlsOptions = _tlsOptions ?? _tcpOptions?.TlsOptions;
|
||||
|
||||
if (_tlsParameters != null)
|
||||
{
|
||||
if (_tlsParameters?.UseTls == true)
|
||||
{
|
||||
var tlsOptions = new MqttClientTlsOptions
|
||||
tlsOptions = new MqttClientTlsOptions
|
||||
{
|
||||
UseTls = true,
|
||||
SslProtocol = _tlsParameters.SslProtocol,
|
||||
@@ -40,28 +50,23 @@ namespace MQTTnet.Client
|
||||
CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
|
||||
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
|
||||
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors,
|
||||
#if WINDOWS_UWP
|
||||
Certificates = _tlsParameters.Certificates?.Select(c => c.ToArray()).ToList(),
|
||||
#else
|
||||
Certificates = _tlsParameters.Certificates?.ToList(),
|
||||
#endif
|
||||
|
||||
ClientCertificatesProvider = _tlsParameters.CertificatesProvider,
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
ApplicationProtocols = _tlsParameters.ApplicationProtocols,
|
||||
#endif
|
||||
};
|
||||
|
||||
if (_tcpOptions != null)
|
||||
{
|
||||
_tcpOptions.TlsOptions = tlsOptions;
|
||||
}
|
||||
else if (_webSocketOptions != null)
|
||||
{
|
||||
_webSocketOptions.TlsOptions = tlsOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_tcpOptions != null)
|
||||
{
|
||||
_tcpOptions.TlsOptions = tlsOptions;
|
||||
}
|
||||
else if (_webSocketOptions != null)
|
||||
{
|
||||
_webSocketOptions.TlsOptions = tlsOptions;
|
||||
}
|
||||
|
||||
if (_proxyOptions != null)
|
||||
{
|
||||
if (_webSocketOptions == null)
|
||||
@@ -75,7 +80,7 @@ namespace MQTTnet.Client
|
||||
_options.ChannelOptions = (IMqttClientChannelOptions)_tcpOptions ?? _webSocketOptions;
|
||||
|
||||
MqttClientOptionsValidator.ThrowIfNotSupported(_options);
|
||||
|
||||
|
||||
return _options;
|
||||
}
|
||||
|
||||
@@ -86,18 +91,31 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean session is used in MQTT versions below 5.0.0. It is the same as setting "CleanStart".
|
||||
/// </summary>
|
||||
public MqttClientOptionsBuilder WithCleanSession(bool value = true)
|
||||
{
|
||||
_options.CleanSession = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean start is used in MQTT versions 5.0.0 and higher. It is the same as setting "CleanSession".
|
||||
/// </summary>
|
||||
public MqttClientOptionsBuilder WithCleanStart(bool value = true)
|
||||
{
|
||||
_options.CleanSession = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithClientId(string value)
|
||||
{
|
||||
_options.ClientId = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
[Obsolete("Use WithTcpServer(... configure) or WithWebSocketServer(... configure) instead.")]
|
||||
public MqttClientOptionsBuilder WithConnectionUri(Uri uri)
|
||||
{
|
||||
if (uri == null)
|
||||
@@ -114,12 +132,16 @@ namespace MQTTnet.Client
|
||||
break;
|
||||
|
||||
case "mqtts":
|
||||
WithTcpServer(uri.Host, port).WithTls();
|
||||
WithTcpServer(uri.Host, port)
|
||||
.WithTlsOptions(
|
||||
o =>
|
||||
{
|
||||
});
|
||||
break;
|
||||
|
||||
case "ws":
|
||||
case "wss":
|
||||
WithWebSocketServer(uri.ToString());
|
||||
WithWebSocketServer(o => o.WithUri(uri.ToString()));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -137,6 +159,7 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
[Obsolete("Use WithTcpServer(... configure) or WithWebSocketServer(... configure) instead.")]
|
||||
public MqttClientOptionsBuilder WithConnectionUri(string uri)
|
||||
{
|
||||
return WithConnectionUri(new Uri(uri, UriKind.Absolute));
|
||||
@@ -188,6 +211,28 @@ namespace MQTTnet.Client
|
||||
return WithKeepAlivePeriod(TimeSpan.Zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Usually the MQTT packets can be send partially. This is done by using multiple TCP packets
|
||||
/// or WebSocket frames etc. Unfortunately not all brokers (like Amazon Web Services (AWS)) do support this feature and
|
||||
/// will close the connection when receiving such packets. If such a service is used this flag must
|
||||
/// be set to _true_.
|
||||
/// </summary>
|
||||
public MqttClientOptionsBuilder WithoutPacketFragmentation()
|
||||
{
|
||||
_options.AllowPacketFragmentation = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The client will not throw an exception when the MQTT server responses with a non success ACK packet.
|
||||
/// This will become the default behavior in future versions of the library.
|
||||
/// </summary>
|
||||
public MqttClientOptionsBuilder WithoutThrowOnNonSuccessfulConnectResponse()
|
||||
{
|
||||
_options.ThrowOnNonSuccessfulConnectResponse = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
|
||||
{
|
||||
if (value == MqttProtocolVersion.Unknown)
|
||||
@@ -199,6 +244,7 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
[Obsolete("Use WithWebSocketServer(... configure) instead.")]
|
||||
public MqttClientOptionsBuilder WithProxy(
|
||||
string address,
|
||||
string username = null,
|
||||
@@ -220,6 +266,7 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
[Obsolete("Use WithWebSocketServer(... configure) instead.")]
|
||||
public MqttClientOptionsBuilder WithProxy(Action<MqttClientWebSocketProxyOptions> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null)
|
||||
@@ -266,7 +313,7 @@ namespace MQTTnet.Client
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public MqttClientOptionsBuilder WithTcpServer(Action<MqttClientTcpOptions> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null)
|
||||
@@ -290,17 +337,20 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
[Obsolete("Use WithTlsOptions(... configure) instead.")]
|
||||
public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters)
|
||||
{
|
||||
_tlsParameters = parameters;
|
||||
return this;
|
||||
}
|
||||
|
||||
[Obsolete("Use WithTlsOptions(... configure) instead.")]
|
||||
public MqttClientOptionsBuilder WithTls()
|
||||
{
|
||||
return WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = true });
|
||||
}
|
||||
|
||||
[Obsolete("Use WithTlsOptions(... configure) instead.")]
|
||||
public MqttClientOptionsBuilder WithTls(Action<MqttClientOptionsBuilderTlsParameters> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null)
|
||||
@@ -317,6 +367,26 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTlsOptions(MqttClientTlsOptions tlsOptions)
|
||||
{
|
||||
_tlsOptions = tlsOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTlsOptions(Action<MqttClientTlsOptionsBuilder> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var builder = new MqttClientTlsOptionsBuilder();
|
||||
configure.Invoke(builder);
|
||||
|
||||
_tlsOptions = builder.Build();
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTopicAliasMaximum(ushort topicAliasMaximum)
|
||||
{
|
||||
_options.TopicAliasMaximum = topicAliasMaximum;
|
||||
@@ -348,6 +418,7 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
[Obsolete("Use WithWebSocketServer(... configure) instead.")]
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(string uri, MqttClientOptionsBuilderWebSocketParameters parameters = null)
|
||||
{
|
||||
_webSocketOptions = new MqttClientWebSocketOptions
|
||||
@@ -360,6 +431,21 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(Action<MqttClientWebSocketOptionsBuilder> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var webSocketOptionsBuilder = new MqttClientWebSocketOptionsBuilder();
|
||||
configure.Invoke(webSocketOptionsBuilder);
|
||||
|
||||
_webSocketOptions = webSocketOptionsBuilder.Build();
|
||||
return this;
|
||||
}
|
||||
|
||||
[Obsolete("Use WithWebSocketServer(... configure) instead.")]
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(Action<MqttClientWebSocketOptions> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null)
|
||||
@@ -373,38 +459,74 @@ namespace MQTTnet.Client
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillContentType(string willContentType)
|
||||
{
|
||||
_options.WillContentType = willContentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillCorrelationData(byte[] willCorrelationData)
|
||||
{
|
||||
_options.WillCorrelationData = willCorrelationData;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillDelayInterval(uint willDelayInterval)
|
||||
{
|
||||
_options.WillDelayInterval = willDelayInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillMessageExpiryInterval(uint willMessageExpiryInterval)
|
||||
{
|
||||
_options.WillMessageExpiryInterval = willMessageExpiryInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillPayload(byte[] willPayload)
|
||||
{
|
||||
_options.WillPayload = willPayload;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public MqttClientOptionsBuilder WithWillPayload(ArraySegment<byte> willPayload)
|
||||
{
|
||||
if (willPayload.Count == 0)
|
||||
{
|
||||
_options.WillPayload = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
_options.WillPayload = willPayload.ToArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillPayload(string willPayload)
|
||||
{
|
||||
if (string.IsNullOrEmpty(willPayload))
|
||||
{
|
||||
return WithWillPayload((byte[])null);
|
||||
}
|
||||
|
||||
|
||||
_options.WillPayload = Encoding.UTF8.GetBytes(willPayload);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillPayloadFormatIndicator(MqttPayloadFormatIndicator willPayloadFormatIndicator)
|
||||
{
|
||||
_options.WillPayloadFormatIndicator = willPayloadFormatIndicator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel willQualityOfServiceLevel)
|
||||
{
|
||||
_options.WillQualityOfServiceLevel = willQualityOfServiceLevel;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillTopic(string willTopic)
|
||||
public MqttClientOptionsBuilder WithWillResponseTopic(string willResponseTopic)
|
||||
{
|
||||
_options.WillTopic = willTopic;
|
||||
_options.WillResponseTopic = willResponseTopic;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -413,38 +535,20 @@ namespace MQTTnet.Client
|
||||
_options.WillRetain = willRetain;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillContentType(string willContentType)
|
||||
|
||||
public MqttClientOptionsBuilder WithWillTopic(string willTopic)
|
||||
{
|
||||
_options.WillContentType = willContentType;
|
||||
_options.WillTopic = willTopic;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillCorrelationData(byte[] willCorrelationData)
|
||||
{
|
||||
_options.WillCorrelationData = willCorrelationData;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillResponseTopic(string willResponseTopic)
|
||||
{
|
||||
_options.WillResponseTopic = willResponseTopic;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillPayloadFormatIndicator(MqttPayloadFormatIndicator willPayloadFormatIndicator)
|
||||
{
|
||||
_options.WillPayloadFormatIndicator = willPayloadFormatIndicator;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public MqttClientOptionsBuilder WithWillUserProperty(string name, string value)
|
||||
{
|
||||
if (_options.WillUserProperties == null)
|
||||
{
|
||||
_options.WillUserProperties = new List<MqttUserProperty>();
|
||||
}
|
||||
|
||||
|
||||
_options.WillUserProperties.Add(new MqttUserProperty(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
[Obsolete("Use methods from MqttClientOptionsBuilder instead.")]
|
||||
public sealed class MqttClientOptionsBuilderTlsParameters
|
||||
{
|
||||
IEnumerable<System.Security.Cryptography.X509Certificates.X509Certificate> _obsoleteCertificates;
|
||||
|
||||
public bool UseTls { get; set; }
|
||||
|
||||
public Func<MqttClientCertificateValidationEventArgs, bool> CertificateValidationHandler { get; set; }
|
||||
@@ -24,7 +26,24 @@ namespace MQTTnet.Client
|
||||
#if WINDOWS_UWP
|
||||
public IEnumerable<IEnumerable<byte>> Certificates { get; set; }
|
||||
#else
|
||||
public IEnumerable<X509Certificate> Certificates { get; set; }
|
||||
[Obsolete("Use CertificatesProvider instead.")]
|
||||
public IEnumerable<System.Security.Cryptography.X509Certificates.X509Certificate> Certificates
|
||||
{
|
||||
get => _obsoleteCertificates;
|
||||
set
|
||||
{
|
||||
_obsoleteCertificates = value;
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
CertificatesProvider = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
CertificatesProvider = new DefaultMqttCertificatesProvider(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
@@ -36,5 +55,7 @@ namespace MQTTnet.Client
|
||||
public bool IgnoreCertificateChainErrors { get; set; }
|
||||
|
||||
public bool IgnoreCertificateRevocationErrors { get; set; }
|
||||
|
||||
public IMqttClientCertificatesProvider CertificatesProvider { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,13 @@
|
||||
// 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.Net;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
[Obsolete("Use dedicated methods in MqttClientOptionsBuilder.")]
|
||||
public class MqttClientOptionsBuilderWebSocketParameters
|
||||
{
|
||||
public IDictionary<string, string> RequestHeaders { get; set; }
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace MQTTnet.Client
|
||||
return options.Port.Value;
|
||||
}
|
||||
|
||||
return !options.TlsOptions.UseTls ? 1883 : 8883;
|
||||
return !(options.TlsOptions?.UseTls ?? false) ? 1883 : 8883;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
@@ -23,18 +24,30 @@ namespace MQTTnet.Client
|
||||
|
||||
public X509RevocationMode RevocationMode { get; set; } = X509RevocationMode.Online;
|
||||
|
||||
#if WINDOWS_UWP
|
||||
public List<byte[]> Certificates { get; set; }
|
||||
#else
|
||||
public List<X509Certificate> Certificates { get; set; }
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Gets or sets the provider for certificates.
|
||||
/// This provider gets called whenever the client wants to connect
|
||||
/// with the server and requires certificates for authentication.
|
||||
/// The implementation may return different certificates each time.
|
||||
/// </summary>
|
||||
public IMqttClientCertificatesProvider ClientCertificatesProvider { get; set; }
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
public List<System.Net.Security.SslApplicationProtocol> ApplicationProtocols { get; set; }
|
||||
|
||||
public System.Net.Security.CipherSuitesPolicy CipherSuitesPolicy { get; set; }
|
||||
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
|
||||
|
||||
public CipherSuitesPolicy CipherSuitesPolicy { get; set; }
|
||||
|
||||
public EncryptionPolicy EncryptionPolicy { get; set; } = EncryptionPolicy.RequireEncryption;
|
||||
|
||||
public bool AllowRenegotiation { get; set; } = true;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target host.
|
||||
/// If the value is null or empty the same host as the TCP socket host will be used.
|
||||
/// </summary>
|
||||
public string TargetHost { get; set; }
|
||||
|
||||
#if NET48 || NETCOREAPP3_1_OR_GREATER
|
||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12 | SslProtocols.Tls13;
|
||||
#else
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
using System.Net.Security;
|
||||
#endif
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientTlsOptionsBuilder
|
||||
{
|
||||
readonly MqttClientTlsOptions _tlsOptions = new MqttClientTlsOptions
|
||||
{
|
||||
// If someone used this builder the change is very very high that TLS
|
||||
// should be actually used.
|
||||
UseTls = true
|
||||
};
|
||||
|
||||
public MqttClientTlsOptions Build()
|
||||
{
|
||||
return _tlsOptions;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder UseTls(bool useTls = true)
|
||||
{
|
||||
_tlsOptions.UseTls = useTls;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithAllowUntrustedCertificates(bool allowUntrustedCertificates = true)
|
||||
{
|
||||
_tlsOptions.AllowUntrustedCertificates = allowUntrustedCertificates;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithCertificateValidationHandler(Func<MqttClientCertificateValidationEventArgs, bool> certificateValidationHandler)
|
||||
{
|
||||
if (certificateValidationHandler == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(certificateValidationHandler));
|
||||
}
|
||||
|
||||
_tlsOptions.CertificateValidationHandler = certificateValidationHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithClientCertificates(IEnumerable<X509Certificate2> certificates)
|
||||
{
|
||||
if (certificates == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(certificates));
|
||||
}
|
||||
|
||||
_tlsOptions.ClientCertificatesProvider = new DefaultMqttCertificatesProvider(certificates);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithClientCertificates(X509Certificate2Collection certificates)
|
||||
{
|
||||
if (certificates == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(certificates));
|
||||
}
|
||||
|
||||
_tlsOptions.ClientCertificatesProvider = new DefaultMqttCertificatesProvider(certificates);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithClientCertificatesProvider(IMqttClientCertificatesProvider clientCertificatesProvider)
|
||||
{
|
||||
_tlsOptions.ClientCertificatesProvider = clientCertificatesProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithIgnoreCertificateChainErrors(bool ignoreCertificateChainErrors = true)
|
||||
{
|
||||
_tlsOptions.IgnoreCertificateChainErrors = ignoreCertificateChainErrors;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithIgnoreCertificateRevocationErrors(bool ignoreCertificateRevocationErrors = true)
|
||||
{
|
||||
_tlsOptions.IgnoreCertificateRevocationErrors = ignoreCertificateRevocationErrors;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithRevocationMode(X509RevocationMode revocationMode)
|
||||
{
|
||||
_tlsOptions.RevocationMode = revocationMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithSslProtocols(SslProtocols sslProtocols)
|
||||
{
|
||||
_tlsOptions.SslProtocol = sslProtocols;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithTargetHost(string targetHost)
|
||||
{
|
||||
_tlsOptions.TargetHost = targetHost;
|
||||
return this;
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
public MqttClientTlsOptionsBuilder WithAllowRenegotiation(bool allowRenegotiation = true)
|
||||
{
|
||||
_tlsOptions.AllowRenegotiation = allowRenegotiation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithApplicationProtocols(List<SslApplicationProtocol> applicationProtocols)
|
||||
{
|
||||
_tlsOptions.ApplicationProtocols = applicationProtocols;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithCipherSuitesPolicy(CipherSuitesPolicy cipherSuitePolicy)
|
||||
{
|
||||
_tlsOptions.CipherSuitesPolicy = cipherSuitePolicy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientTlsOptionsBuilder WithCipherSuitesPolicy(EncryptionPolicy encryptionPolicy)
|
||||
{
|
||||
_tlsOptions.EncryptionPolicy = encryptionPolicy;
|
||||
return this;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,19 @@
|
||||
// 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.Net;
|
||||
using System.Net.WebSockets;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientWebSocketOptions : IMqttClientChannelOptions
|
||||
{
|
||||
public CookieContainer CookieContainer { get; set; }
|
||||
|
||||
|
||||
public ICredentials Credentials { get; set; }
|
||||
|
||||
public MqttClientWebSocketProxyOptions ProxyOptions { get; set; }
|
||||
|
||||
public IDictionary<string, string> RequestHeaders { get; set; }
|
||||
@@ -18,12 +22,34 @@ namespace MQTTnet.Client
|
||||
public ICollection<string> SubProtocols { get; set; } = new List<string> { "mqtt" };
|
||||
|
||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
||||
|
||||
|
||||
public string Uri { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Uri;
|
||||
}
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
#if !WINDOWS_UWP
|
||||
/// <summary>
|
||||
/// Gets or sets the keep alive interval for the Web Socket connection.
|
||||
/// This is not related to the keep alive interval for the MQTT protocol.
|
||||
/// </summary>
|
||||
public TimeSpan KeepAliveInterval { get; set; } = WebSocket.DefaultKeepAliveInterval;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the default (system) credentials should be used when connecting via Web Socket connection.
|
||||
/// This is not related to the credentials which are used for the MQTT protocol.
|
||||
/// </summary>
|
||||
public bool UseDefaultCredentials { get; set; }
|
||||
#else
|
||||
/// <summary>
|
||||
/// Gets or sets the keep alive interval for the Web Socket connection.
|
||||
/// This is not related to the keep alive interval for the MQTT protocol.
|
||||
/// </summary>
|
||||
public TimeSpan KeepAliveInterval { get; set; } = TimeSpan.FromSeconds(30);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientWebSocketOptionsBuilder
|
||||
{
|
||||
readonly MqttClientWebSocketOptions _webSocketOptions = new MqttClientWebSocketOptions();
|
||||
|
||||
public MqttClientWebSocketOptions Build()
|
||||
{
|
||||
return _webSocketOptions;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketOptionsBuilder WithCookieContainer(CookieContainer cookieContainer)
|
||||
{
|
||||
_webSocketOptions.CookieContainer = cookieContainer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketOptionsBuilder WithCookieContainer(ICredentials credentials)
|
||||
{
|
||||
_webSocketOptions.Credentials = credentials;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketOptionsBuilder WithProxyOptions(MqttClientWebSocketProxyOptions proxyOptions)
|
||||
{
|
||||
_webSocketOptions.ProxyOptions = proxyOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketOptionsBuilder WithProxyOptions(Action<MqttClientWebSocketProxyOptionsBuilder> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var proxyOptionsBuilder = new MqttClientWebSocketProxyOptionsBuilder();
|
||||
configure.Invoke(proxyOptionsBuilder);
|
||||
|
||||
_webSocketOptions.ProxyOptions = proxyOptionsBuilder.Build();
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketOptionsBuilder WithRequestHeaders(IDictionary<string, string> requestHeaders)
|
||||
{
|
||||
_webSocketOptions.RequestHeaders = requestHeaders;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketOptionsBuilder WithSubProtocols(ICollection<string> subProtocols)
|
||||
{
|
||||
_webSocketOptions.SubProtocols = subProtocols;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketOptionsBuilder WithUri(string uri)
|
||||
{
|
||||
_webSocketOptions.Uri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
public MqttClientWebSocketOptionsBuilder WithKeepAliveInterval(TimeSpan keepAliveInterval)
|
||||
{
|
||||
_webSocketOptions.KeepAliveInterval = keepAliveInterval;
|
||||
return this;
|
||||
}
|
||||
#endif
|
||||
#if !WINDOWS_UWP && !NETSTANDARD1_3
|
||||
public MqttClientWebSocketOptionsBuilder WithUseDefaultCredentials(bool useDefaultCredentials = true)
|
||||
{
|
||||
_webSocketOptions.UseDefaultCredentials = useDefaultCredentials;
|
||||
return this;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ namespace MQTTnet.Client
|
||||
|
||||
public bool BypassOnLocal { get; set; }
|
||||
|
||||
public bool UseDefaultCredentials { get; set; }
|
||||
|
||||
public string[] BypassList { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientWebSocketProxyOptionsBuilder
|
||||
{
|
||||
readonly MqttClientWebSocketProxyOptions _proxyOptions = new MqttClientWebSocketProxyOptions();
|
||||
|
||||
public MqttClientWebSocketProxyOptionsBuilder WithAddress(string address)
|
||||
{
|
||||
_proxyOptions.Address = address;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketProxyOptionsBuilder WithUsername(string username)
|
||||
{
|
||||
_proxyOptions.Username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketProxyOptionsBuilder WithPassword(string password)
|
||||
{
|
||||
_proxyOptions.Password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketProxyOptionsBuilder WithDomain(string domain)
|
||||
{
|
||||
_proxyOptions.Domain = domain;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketProxyOptionsBuilder WithBypassOnLocal(bool bypassOnLocal = true)
|
||||
{
|
||||
_proxyOptions.BypassOnLocal = bypassOnLocal;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketProxyOptionsBuilder WithBypassList(string[] bypassList)
|
||||
{
|
||||
_proxyOptions.BypassList = bypassList;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketProxyOptionsBuilder WithBypassList(IEnumerable<string> bypassList)
|
||||
{
|
||||
_proxyOptions.BypassList = bypassList?.ToArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketProxyOptionsBuilder WithUseDefaultCredentials(bool useDefaultCredentials = true)
|
||||
{
|
||||
_proxyOptions.UseDefaultCredentials = useDefaultCredentials;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientWebSocketProxyOptions Build()
|
||||
{
|
||||
return _proxyOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ namespace MQTTnet.Client
|
||||
/// Gets or sets the reason code.
|
||||
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||
/// </summary>
|
||||
public MqttClientPublishReasonCode ReasonCode { get; } = MqttClientPublishReasonCode.Success;
|
||||
public MqttClientPublishReasonCode ReasonCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason string.
|
||||
|
||||
@@ -42,6 +42,6 @@ namespace MQTTnet.Client
|
||||
/// Gets the user properties which were part of the UNSUBACK packet.
|
||||
/// <remarks>MQTT 5.0.0+ feature.</remarks>
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; }
|
||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ namespace MQTTnet.Diagnostics
|
||||
return "net461";
|
||||
#elif NET472
|
||||
return "net472";
|
||||
#elif NET48
|
||||
return "net48";
|
||||
#elif NETSTANDARD1_3
|
||||
return "netstandard1.3";
|
||||
#elif NETSTANDARD2_0
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace MQTTnet.Formatter
|
||||
return new MqttApplicationMessage
|
||||
{
|
||||
Topic = publishPacket.Topic,
|
||||
Payload = publishPacket.Payload,
|
||||
PayloadSegment = publishPacket.PayloadSegment,
|
||||
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
|
||||
Retain = publishPacket.Retain,
|
||||
Dup = publishPacket.Dup,
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace MQTTnet.Formatter
|
||||
/// </summary>
|
||||
public sealed class MqttBufferWriter
|
||||
{
|
||||
public const uint VariableByteIntegerMaxValue = 268435455;
|
||||
const uint VariableByteIntegerMaxValue = 268435455;
|
||||
const int EncodedStringMaxLength = 65535;
|
||||
|
||||
readonly int _maxBufferSize;
|
||||
|
||||
@@ -112,25 +113,7 @@ namespace MQTTnet.Formatter
|
||||
WriteBinary(propertyWriter._buffer, 0, propertyWriter.Length);
|
||||
}
|
||||
|
||||
public void WriteBinary(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureAdditionalCapacity(count);
|
||||
|
||||
MqttMemoryHelper.Copy(buffer, offset, _buffer, _position, count);
|
||||
IncreasePosition(count);
|
||||
}
|
||||
|
||||
public void WriteBinaryData(byte[] value)
|
||||
public void WriteBinary(byte[] value)
|
||||
{
|
||||
if (value == null || value.Length == 0)
|
||||
{
|
||||
@@ -155,6 +138,24 @@ namespace MQTTnet.Formatter
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteBinary(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureAdditionalCapacity(count);
|
||||
|
||||
MqttMemoryHelper.Copy(buffer, offset, _buffer, _position, count);
|
||||
IncreasePosition(count);
|
||||
}
|
||||
|
||||
public void WriteByte(byte @byte)
|
||||
{
|
||||
EnsureAdditionalCapacity(1);
|
||||
@@ -186,6 +187,13 @@ namespace MQTTnet.Formatter
|
||||
|
||||
var writtenBytes = Encoding.UTF8.GetBytes(value, 0, value.Length, _buffer, _position + 2);
|
||||
|
||||
// From RFC: 1.5.4 UTF-8 Encoded String
|
||||
// Unless stated otherwise all UTF-8 encoded strings can have any length in the range 0 to 65,535 bytes.
|
||||
if (writtenBytes > EncodedStringMaxLength)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"The maximum string length is 65535. The current string has a length of {writtenBytes}.");
|
||||
}
|
||||
|
||||
_buffer[_position] = (byte)(writtenBytes >> 8);
|
||||
_buffer[_position + 1] = (byte)writtenBytes;
|
||||
|
||||
@@ -273,8 +281,7 @@ namespace MQTTnet.Formatter
|
||||
newBufferLength *= 2;
|
||||
}
|
||||
|
||||
// Array.Resize will create a new array and copy the existing
|
||||
// data to the new one.
|
||||
// Array.Resize will create a new array and copy the existing data to the new one.
|
||||
Array.Resize(ref _buffer, newBufferLength);
|
||||
}
|
||||
|
||||
@@ -285,6 +292,8 @@ namespace MQTTnet.Formatter
|
||||
|
||||
if (_position > Length)
|
||||
{
|
||||
// Also extend the position because we reached the end of the
|
||||
// pre allocated buffer.
|
||||
Length = _position;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,54 +2,12 @@
|
||||
// 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.Exceptions;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public static class MqttConnectReasonCodeConverter
|
||||
{
|
||||
public static MqttConnectReasonCode ToConnectReasonCode(MqttConnectReturnCode returnCode)
|
||||
{
|
||||
switch (returnCode)
|
||||
{
|
||||
case MqttConnectReturnCode.ConnectionAccepted:
|
||||
{
|
||||
return MqttConnectReasonCode.Success;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion:
|
||||
{
|
||||
return MqttConnectReasonCode.UnsupportedProtocolVersion;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword:
|
||||
{
|
||||
return MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedIdentifierRejected:
|
||||
{
|
||||
return MqttConnectReasonCode.ClientIdentifierNotValid;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedServerUnavailable:
|
||||
{
|
||||
return MqttConnectReasonCode.ServerUnavailable;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedNotAuthorized:
|
||||
{
|
||||
return MqttConnectReasonCode.NotAuthorized;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MqttConnectReturnCode ToConnectReturnCode(MqttConnectReasonCode reasonCode)
|
||||
{
|
||||
switch (reasonCode)
|
||||
@@ -59,11 +17,13 @@ namespace MQTTnet.Formatter
|
||||
return MqttConnectReturnCode.ConnectionAccepted;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.Banned:
|
||||
case MqttConnectReasonCode.NotAuthorized:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedNotAuthorized;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.BadAuthenticationMethod:
|
||||
case MqttConnectReasonCode.BadUserNameOrPassword:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
|
||||
@@ -79,6 +39,7 @@ namespace MQTTnet.Formatter
|
||||
return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.UseAnotherServer:
|
||||
case MqttConnectReasonCode.ServerUnavailable:
|
||||
case MqttConnectReasonCode.ServerBusy:
|
||||
case MqttConnectReasonCode.ServerMoved:
|
||||
@@ -87,9 +48,7 @@ namespace MQTTnet.Formatter
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3).");
|
||||
}
|
||||
return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,14 @@ namespace MQTTnet.Formatter
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion { get; private set; } = MqttProtocolVersion.Unknown;
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
_bufferWriter.Cleanup();
|
||||
}
|
||||
|
||||
public MqttPacket Decode(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
ThrowIfFormatterNotSet();
|
||||
|
||||
return _formatter.Decode(receivedMqttPacket);
|
||||
}
|
||||
|
||||
@@ -50,11 +54,6 @@ namespace MQTTnet.Formatter
|
||||
return _formatter.Encode(packet);
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
_bufferWriter.Cleanup();
|
||||
}
|
||||
|
||||
public static IMqttPacketFormatter GetMqttPacketFormatter(MqttProtocolVersion protocolVersion, MqttBufferWriter bufferWriter)
|
||||
{
|
||||
if (protocolVersion == MqttProtocolVersion.Unknown)
|
||||
@@ -98,7 +97,7 @@ namespace MQTTnet.Formatter
|
||||
// Remove the mosquitto try_private flag (MQTT 3.1.1 Bridge).
|
||||
// This flag is accepted but not yet used.
|
||||
protocolLevel &= 0x7F;
|
||||
|
||||
|
||||
if (protocolName == "MQTT")
|
||||
{
|
||||
if (protocolLevel == 5)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace MQTTnet.Formatter
|
||||
return new MqttPublishPacket
|
||||
{
|
||||
Topic = publishPacket.Topic,
|
||||
Payload = publishPacket.Payload,
|
||||
PayloadSegment = publishPacket.PayloadSegment,
|
||||
Retain = publishPacket.Retain,
|
||||
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
|
||||
Dup = publishPacket.Dup,
|
||||
@@ -36,7 +36,7 @@ namespace MQTTnet.Formatter
|
||||
var packet = new MqttPublishPacket
|
||||
{
|
||||
Topic = applicationMessage.Topic,
|
||||
Payload = applicationMessage.Payload,
|
||||
PayloadSegment = applicationMessage.PayloadSegment,
|
||||
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
|
||||
Retain = applicationMessage.Retain,
|
||||
Dup = applicationMessage.Dup,
|
||||
@@ -65,10 +65,16 @@ namespace MQTTnet.Formatter
|
||||
throw new MqttProtocolViolationException("The CONNECT packet contains no will message (WillFlag).");
|
||||
}
|
||||
|
||||
ArraySegment<byte> willMessageBuffer = default;
|
||||
if (connectPacket.WillMessage?.Length > 0)
|
||||
{
|
||||
willMessageBuffer = new ArraySegment<byte>(connectPacket.WillMessage);
|
||||
}
|
||||
|
||||
var packet = new MqttPublishPacket
|
||||
{
|
||||
Topic = connectPacket.WillTopic,
|
||||
Payload = connectPacket.WillMessage,
|
||||
PayloadSegment = willMessageBuffer,
|
||||
QualityOfServiceLevel = connectPacket.WillQoS,
|
||||
Retain = connectPacket.WillRetain,
|
||||
ContentType = connectPacket.WillContentType,
|
||||
|
||||
@@ -54,12 +54,12 @@ namespace MQTTnet.Formatter.V3
|
||||
return DecodePubRelPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.PubComp:
|
||||
return DecodePubCompPacket(receivedMqttPacket.Body);
|
||||
|
||||
|
||||
case MqttControlPacketType.PingReq:
|
||||
return MqttPingReqPacket.Instance;
|
||||
case MqttControlPacketType.PingResp:
|
||||
return MqttPingRespPacket.Instance;
|
||||
|
||||
|
||||
case MqttControlPacketType.Connect:
|
||||
return DecodeConnectPacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.ConnAck:
|
||||
@@ -73,7 +73,7 @@ namespace MQTTnet.Formatter.V3
|
||||
}
|
||||
case MqttControlPacketType.Disconnect:
|
||||
return DisconnectPacket;
|
||||
|
||||
|
||||
case MqttControlPacketType.Subscribe:
|
||||
return DecodeSubscribePacket(receivedMqttPacket.Body);
|
||||
case MqttControlPacketType.SubAck:
|
||||
@@ -103,9 +103,10 @@ namespace MQTTnet.Formatter.V3
|
||||
var remainingLength = (uint)(_bufferWriter.Length - 5);
|
||||
|
||||
var publishPacket = packet as MqttPublishPacket;
|
||||
if (publishPacket?.Payload != null)
|
||||
var payloadSegment = publishPacket?.PayloadSegment;
|
||||
if (payloadSegment != null)
|
||||
{
|
||||
remainingLength += (uint)publishPacket.Payload.Length;
|
||||
remainingLength += (uint)payloadSegment.Value.Count;
|
||||
}
|
||||
|
||||
var remainingLengthSize = MqttBufferWriter.GetVariableByteIntegerSize(remainingLength);
|
||||
@@ -119,18 +120,13 @@ namespace MQTTnet.Formatter.V3
|
||||
_bufferWriter.WriteVariableByteInteger(remainingLength);
|
||||
|
||||
var buffer = _bufferWriter.GetBuffer();
|
||||
|
||||
var firstSegment = new ArraySegment<byte>(buffer, headerOffset, _bufferWriter.Length - headerOffset);
|
||||
|
||||
if (publishPacket?.Payload != null)
|
||||
{
|
||||
var payloadSegment = new ArraySegment<byte>(publishPacket.Payload, 0, publishPacket.Payload.Length);
|
||||
return new MqttPacketBuffer(firstSegment, payloadSegment);
|
||||
}
|
||||
|
||||
return new MqttPacketBuffer(firstSegment);
|
||||
return payloadSegment == null
|
||||
? new MqttPacketBuffer(firstSegment)
|
||||
: new MqttPacketBuffer(firstSegment, payloadSegment.Value);
|
||||
}
|
||||
|
||||
|
||||
MqttPacket DecodeConnAckPacket(ArraySegment<byte> body)
|
||||
{
|
||||
ThrowIfBodyIsEmpty(body);
|
||||
@@ -177,7 +173,7 @@ namespace MQTTnet.Formatter.V3
|
||||
|
||||
var tryPrivate = (protocolVersion & 0x80) > 0;
|
||||
protocolVersion &= 0x7F;
|
||||
|
||||
|
||||
if (protocolVersion != 3 && protocolVersion != 4)
|
||||
{
|
||||
throw new MqttProtocolViolationException("MQTT protocol version do not match MQTT v3.");
|
||||
@@ -282,7 +278,7 @@ namespace MQTTnet.Formatter.V3
|
||||
|
||||
if (!_bufferReader.EndOfStream)
|
||||
{
|
||||
packet.Payload = _bufferReader.ReadRemainingData();
|
||||
packet.PayloadSegment = new ArraySegment<byte>(_bufferReader.ReadRemainingData());
|
||||
}
|
||||
|
||||
return packet;
|
||||
@@ -421,7 +417,7 @@ namespace MQTTnet.Formatter.V3
|
||||
{
|
||||
protocolVersion |= 0x80;
|
||||
}
|
||||
|
||||
|
||||
bufferWriter.WriteByte((byte)protocolVersion);
|
||||
|
||||
byte connectFlags = 0x0;
|
||||
@@ -463,7 +459,7 @@ namespace MQTTnet.Formatter.V3
|
||||
if (packet.WillFlag)
|
||||
{
|
||||
bufferWriter.WriteString(packet.WillTopic);
|
||||
bufferWriter.WriteBinaryData(packet.WillMessage);
|
||||
bufferWriter.WriteBinary(packet.WillMessage);
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
@@ -473,7 +469,7 @@ namespace MQTTnet.Formatter.V3
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
bufferWriter.WriteBinaryData(packet.Password);
|
||||
bufferWriter.WriteBinary(packet.Password);
|
||||
}
|
||||
|
||||
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
@@ -525,7 +521,7 @@ namespace MQTTnet.Formatter.V3
|
||||
if (packet.WillFlag)
|
||||
{
|
||||
bufferWriter.WriteString(packet.WillTopic);
|
||||
bufferWriter.WriteBinaryData(packet.WillMessage);
|
||||
bufferWriter.WriteBinary(packet.WillMessage);
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
@@ -535,7 +531,7 @@ namespace MQTTnet.Formatter.V3
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
bufferWriter.WriteBinaryData(packet.Password);
|
||||
bufferWriter.WriteBinary(packet.Password);
|
||||
}
|
||||
|
||||
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
|
||||
@@ -130,6 +130,9 @@ namespace MQTTnet.Formatter.V5
|
||||
MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce
|
||||
};
|
||||
|
||||
// Also set the return code of MQTT 3.1.1 for backward compatibility and debugging purposes.
|
||||
packet.ReturnCode = MqttConnectReasonCodeConverter.ToConnectReturnCode(packet.ReasonCode);
|
||||
|
||||
var propertiesReader = new MqttV5PropertiesReader(_bufferReader);
|
||||
while (propertiesReader.MoveNext())
|
||||
{
|
||||
@@ -528,7 +531,7 @@ namespace MQTTnet.Formatter.V5
|
||||
|
||||
if (!_bufferReader.EndOfStream)
|
||||
{
|
||||
packet.Payload = _bufferReader.ReadRemainingData();
|
||||
packet.PayloadSegment = new ArraySegment<byte>(_bufferReader.ReadRemainingData());
|
||||
}
|
||||
|
||||
return packet;
|
||||
|
||||
@@ -37,9 +37,11 @@ namespace MQTTnet.Formatter.V5
|
||||
var remainingLength = (uint)_bufferWriter.Length - 5;
|
||||
|
||||
var publishPacket = packet as MqttPublishPacket;
|
||||
if (publishPacket?.Payload != null)
|
||||
var payloadSegment = publishPacket?.PayloadSegment;
|
||||
|
||||
if (payloadSegment != null)
|
||||
{
|
||||
remainingLength += (uint)publishPacket.Payload.Length;
|
||||
remainingLength += (uint)payloadSegment.Value.Count;
|
||||
}
|
||||
|
||||
var remainingLengthSize = MqttBufferWriter.GetVariableByteIntegerSize(remainingLength);
|
||||
@@ -53,18 +55,13 @@ namespace MQTTnet.Formatter.V5
|
||||
_bufferWriter.WriteVariableByteInteger(remainingLength);
|
||||
|
||||
var buffer = _bufferWriter.GetBuffer();
|
||||
|
||||
var firstSegment = new ArraySegment<byte>(buffer, headerOffset, _bufferWriter.Length - headerOffset);
|
||||
|
||||
if (publishPacket?.Payload != null)
|
||||
{
|
||||
var payloadSegment = new ArraySegment<byte>(publishPacket.Payload, 0, publishPacket.Payload.Length);
|
||||
return new MqttPacketBuffer(firstSegment, payloadSegment);
|
||||
}
|
||||
|
||||
return new MqttPacketBuffer(firstSegment);
|
||||
return payloadSegment == null
|
||||
? new MqttPacketBuffer(firstSegment)
|
||||
: new MqttPacketBuffer(firstSegment, payloadSegment.Value);
|
||||
}
|
||||
|
||||
|
||||
byte EncodeAuthPacket(MqttAuthPacket packet)
|
||||
{
|
||||
_bufferWriter.WriteByte((byte)packet.ReasonCode);
|
||||
@@ -189,7 +186,7 @@ namespace MQTTnet.Formatter.V5
|
||||
_propertiesWriter.Reset();
|
||||
|
||||
_bufferWriter.WriteString(packet.WillTopic);
|
||||
_bufferWriter.WriteBinaryData(packet.WillMessage);
|
||||
_bufferWriter.WriteBinary(packet.WillMessage);
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
@@ -199,7 +196,7 @@ namespace MQTTnet.Formatter.V5
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
_bufferWriter.WriteBinaryData(packet.Password);
|
||||
_bufferWriter.WriteBinary(packet.Password);
|
||||
}
|
||||
|
||||
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
|
||||
@@ -335,7 +335,7 @@ namespace MQTTnet.Formatter.V5
|
||||
}
|
||||
|
||||
_bufferWriter.WriteByte((byte)id);
|
||||
_bufferWriter.WriteBinaryData(value);
|
||||
_bufferWriter.WriteBinary(value);
|
||||
}
|
||||
|
||||
void WriteAsFourByteInteger(MqttPropertyId id, uint value)
|
||||
|
||||
@@ -40,7 +40,12 @@ namespace MQTTnet.Implementations
|
||||
|
||||
var bufferWriter = new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax);
|
||||
var packetFormatterAdapter = new MqttPacketFormatterAdapter(options.ProtocolVersion, bufferWriter);
|
||||
return new MqttChannelAdapter(channel, packetFormatterAdapter, packetInspector, logger);
|
||||
|
||||
return new MqttChannelAdapter(channel, packetFormatterAdapter, logger)
|
||||
{
|
||||
AllowPacketFragmentation = options.AllowPacketFragmentation,
|
||||
PacketInspector = packetInspector
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
@@ -18,6 +16,7 @@ using Windows.Security.Cryptography.Certificates;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Server;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
@@ -126,17 +125,19 @@ namespace MQTTnet.Implementations
|
||||
|
||||
private static Certificate LoadCertificate(IMqttClientChannelOptions options)
|
||||
{
|
||||
if (options.TlsOptions.Certificates == null || !options.TlsOptions.Certificates.Any())
|
||||
var certificates = options.TlsOptions?.ClientCertificatesProvider?.GetCertificates();
|
||||
|
||||
if (certificates == null || certificates.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (options.TlsOptions.Certificates.Count > 1)
|
||||
if (certificates.Count > 1)
|
||||
{
|
||||
throw new NotSupportedException("Only one client certificate is supported when using 'uap10.0'.");
|
||||
}
|
||||
|
||||
return new Certificate(options.TlsOptions.Certificates.First().AsBuffer());
|
||||
return new Certificate(certificates[0].Export(X509ContentType.Cert).AsBuffer());
|
||||
}
|
||||
|
||||
private IEnumerable<ChainValidationResult> ResolveIgnorableServerCertificateErrors()
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
// 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;
|
||||
@@ -14,6 +11,9 @@ 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;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
@@ -73,7 +73,7 @@ namespace MQTTnet.Implementations
|
||||
{
|
||||
socket.Bind(_tcpOptions.LocalEndpoint);
|
||||
}
|
||||
|
||||
|
||||
socket.ReceiveBufferSize = _tcpOptions.BufferSize;
|
||||
socket.SendBufferSize = _tcpOptions.BufferSize;
|
||||
socket.SendTimeout = (int)_clientOptions.Timeout.TotalMilliseconds;
|
||||
@@ -100,6 +100,12 @@ namespace MQTTnet.Implementations
|
||||
|
||||
if (_tcpOptions.TlsOptions?.UseTls == true)
|
||||
{
|
||||
var targetHost = _tcpOptions.TlsOptions.TargetHost;
|
||||
if (string.IsNullOrEmpty(targetHost))
|
||||
{
|
||||
targetHost = _tcpOptions.Server;
|
||||
}
|
||||
|
||||
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
||||
try
|
||||
{
|
||||
@@ -111,14 +117,16 @@ namespace MQTTnet.Implementations
|
||||
EnabledSslProtocols = _tcpOptions.TlsOptions.SslProtocol,
|
||||
CertificateRevocationCheckMode =
|
||||
_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : _tcpOptions.TlsOptions.RevocationMode,
|
||||
TargetHost = _tcpOptions.Server,
|
||||
CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy
|
||||
TargetHost = targetHost,
|
||||
CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy,
|
||||
EncryptionPolicy = _tcpOptions.TlsOptions.EncryptionPolicy,
|
||||
AllowRenegotiation = _tcpOptions.TlsOptions.AllowRenegotiation
|
||||
};
|
||||
|
||||
await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
|
||||
#else
|
||||
await sslStream.AuthenticateAsClientAsync(
|
||||
_tcpOptions.Server,
|
||||
targetHost,
|
||||
LoadCertificates(),
|
||||
_tcpOptions.TlsOptions.SslProtocol,
|
||||
!_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors)
|
||||
@@ -290,18 +298,7 @@ namespace MQTTnet.Implementations
|
||||
|
||||
X509CertificateCollection LoadCertificates()
|
||||
{
|
||||
var certificates = new X509CertificateCollection();
|
||||
if (_tcpOptions.TlsOptions.Certificates == null)
|
||||
{
|
||||
return certificates;
|
||||
}
|
||||
|
||||
foreach (var certificate in _tcpOptions.TlsOptions.Certificates)
|
||||
{
|
||||
certificates.Add(certificate);
|
||||
}
|
||||
|
||||
return certificates;
|
||||
return _tcpOptions.TlsOptions.ClientCertificatesProvider?.GetCertificates();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace MQTTnet.Implementations
|
||||
var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter);
|
||||
var tcpChannel = new MqttTcpChannel(args.Socket, clientCertificate, _options);
|
||||
|
||||
using (var clientAdapter = new MqttChannelAdapter(tcpChannel, packetFormatterAdapter, null, _rootLogger))
|
||||
using (var clientAdapter = new MqttChannelAdapter(tcpChannel, packetFormatterAdapter, _rootLogger))
|
||||
{
|
||||
await clientHandler(clientAdapter).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -1,44 +1,51 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#if !WINDOWS_UWP
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Server;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public sealed class MqttTcpServerAdapter : IMqttServerAdapter
|
||||
{
|
||||
readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>();
|
||||
|
||||
CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
MqttServerOptions _serverOptions;
|
||||
CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public bool TreatSocketOpeningErrorAsWarning { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||
{
|
||||
if (_cancellationTokenSource != null) throw new InvalidOperationException("Server is already started.");
|
||||
if (_cancellationTokenSource != null)
|
||||
{
|
||||
throw new InvalidOperationException("Server is already started.");
|
||||
}
|
||||
|
||||
_serverOptions = options;
|
||||
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
if (options.DefaultEndpointOptions.IsEnabled)
|
||||
{
|
||||
RegisterListeners(options.DefaultEndpointOptions, null, logger, _cancellationTokenSource.Token);
|
||||
RegisterListeners(options.DefaultEndpointOptions, logger, _cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
if (options.TlsEndpointOptions?.IsEnabled == true)
|
||||
@@ -48,13 +55,7 @@ namespace MQTTnet.Implementations
|
||||
throw new ArgumentException("TLS certificate is not set.");
|
||||
}
|
||||
|
||||
var tlsCertificate = options.TlsEndpointOptions.CertificateProvider.GetCertificate();
|
||||
if (!tlsCertificate.HasPrivateKey)
|
||||
{
|
||||
throw new InvalidOperationException("The certificate for TLS encryption must contain the private key.");
|
||||
}
|
||||
|
||||
RegisterListeners(options.TlsEndpointOptions, tlsCertificate, logger, _cancellationTokenSource.Token);
|
||||
RegisterListeners(options.TlsEndpointOptions, logger, _cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
@@ -66,11 +67,6 @@ namespace MQTTnet.Implementations
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
try
|
||||
@@ -91,11 +87,22 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterListeners(MqttServerTcpEndpointBaseOptions tcpEndpointOptions, X509Certificate2 tlsCertificate, IMqttNetLogger logger, CancellationToken cancellationToken)
|
||||
Task OnClientAcceptedAsync(IMqttChannelAdapter channelAdapter)
|
||||
{
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler == null)
|
||||
{
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
return clientHandler(channelAdapter);
|
||||
}
|
||||
|
||||
void RegisterListeners(MqttServerTcpEndpointBaseOptions tcpEndpointOptions, IMqttNetLogger logger, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!tcpEndpointOptions.BoundInterNetworkAddress.Equals(IPAddress.None))
|
||||
{
|
||||
var listenerV4 = new MqttTcpServerListener(AddressFamily.InterNetwork, _serverOptions, tcpEndpointOptions, tlsCertificate, logger)
|
||||
var listenerV4 = new MqttTcpServerListener(AddressFamily.InterNetwork, _serverOptions, tcpEndpointOptions, logger)
|
||||
{
|
||||
ClientHandler = OnClientAcceptedAsync
|
||||
};
|
||||
@@ -108,7 +115,7 @@ namespace MQTTnet.Implementations
|
||||
|
||||
if (!tcpEndpointOptions.BoundInterNetworkV6Address.Equals(IPAddress.None))
|
||||
{
|
||||
var listenerV6 = new MqttTcpServerListener(AddressFamily.InterNetworkV6, _serverOptions, tcpEndpointOptions, tlsCertificate, logger)
|
||||
var listenerV6 = new MqttTcpServerListener(AddressFamily.InterNetworkV6, _serverOptions, tcpEndpointOptions, logger)
|
||||
{
|
||||
ClientHandler = OnClientAcceptedAsync
|
||||
};
|
||||
@@ -119,17 +126,6 @@ namespace MQTTnet.Implementations
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Task OnClientAcceptedAsync(IMqttChannelAdapter channelAdapter)
|
||||
{
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler == null)
|
||||
{
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
return clientHandler(channelAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user