diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec
index 40f5616..82233fd 100644
--- a/Build/MQTTnet.nuspec
+++ b/Build/MQTTnet.nuspec
@@ -14,12 +14,12 @@
* [Core] Renamed some topic filter relevant classes (BREAKING CHANGE!).
* [Core] Improved task management for UWP connections (thanks to @xgstation).
-* [LowLevelMqttClient]
* [Client] Added method to trigger PING/PONG manually (connection check etc.).
+* [Client] Added support for certificate validation callback when using Web Sockets (requires netstandard2.1+).
+* [Client] Fixed a memory leak when web socket based connections trying to reconnect with an offline server.
* [ManagedClient] Added method to trigger PING/PONG manually (connection check etc.).
-* [Server]
-* [MQTTnet.AspNetCore] improved compatibility with AspNetCore 3.1
-* [MQTTnet.Server]
+* [MQTTnet.AspNetCore] improved compatibility with AspNetCore 3.1.
+* [MQTTnet.Server] Fixed wrong version output.
Copyright Christian Kratky 2016-2020
MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin
@@ -36,6 +36,12 @@
+
+
+
+
+
+
@@ -55,6 +61,9 @@
+
+
+
diff --git a/Build/build.ps1 b/Build/build.ps1
index 1fe4ae3..4eea1dd 100644
--- a/Build/build.ps1
+++ b/Build/build.ps1
@@ -28,6 +28,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
+&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the ASP.NET Core 2.0 extension
@@ -38,6 +39,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
+&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the Managed Client extension
@@ -45,6 +47,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
+&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the WebSocket4Net extension
@@ -52,6 +55,7 @@ vstest.console.exe ..\Tests\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\M
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
+&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.1" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Create NuGet packages.
diff --git a/Source/MQTTnet/Client/MqttClient.cs b/Source/MQTTnet/Client/MqttClient.cs
index 52929ef..4ace908 100644
--- a/Source/MQTTnet/Client/MqttClient.cs
+++ b/Source/MQTTnet/Client/MqttClient.cs
@@ -61,7 +61,7 @@ namespace MQTTnet.Client
{
get
{
- return _isConnected || Interlocked.Read(ref _isDisconnectPending) != 0;
+ return _isConnected && Interlocked.Read(ref _isDisconnectPending) == 0;
}
}
diff --git a/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs b/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs
new file mode 100644
index 0000000..3827f05
--- /dev/null
+++ b/Source/MQTTnet/Client/Options/MqttClientCertificateValidationCallbackContext.cs
@@ -0,0 +1,16 @@
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+
+namespace MQTTnet.Client.Options
+{
+ public class MqttClientCertificateValidationCallbackContext
+ {
+ public X509Certificate Certificate { get; set; }
+
+ public X509Chain Chain { get; set; }
+
+ public SslPolicyErrors SslPolicyErrors { get; set; }
+
+ public IMqttClientChannelOptions ClientOptions { get; set; }
+ }
+}
diff --git a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs
index b301819..316e5b5 100644
--- a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs
+++ b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilder.cs
@@ -262,6 +262,7 @@ namespace MQTTnet.Client.Options
Certificates = _tlsParameters.Certificates?.ToList(),
#endif
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
+ CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors
};
diff --git a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilderTlsParameters.cs b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilderTlsParameters.cs
index d1854ff..fd9aad0 100644
--- a/Source/MQTTnet/Client/Options/MqttClientOptionsBuilderTlsParameters.cs
+++ b/Source/MQTTnet/Client/Options/MqttClientOptionsBuilderTlsParameters.cs
@@ -10,12 +10,15 @@ namespace MQTTnet.Client.Options
{
public bool UseTls { get; set; }
+ [Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
public Func CertificateValidationCallback
{
get;
set;
}
+ public Func CertificateValidationHandler { get; set; }
+
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
#if WINDOWS_UWP
@@ -23,7 +26,6 @@ namespace MQTTnet.Client.Options
#else
public IEnumerable Certificates { get; set; }
#endif
-
public bool AllowUntrustedCertificates { get; set; }
diff --git a/Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs b/Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs
index 0d1a3a5..b1b3fe8 100644
--- a/Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs
+++ b/Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs
@@ -15,6 +15,7 @@ namespace MQTTnet.Client.Options
public bool IgnoreCertificateChainErrors { get; set; }
public bool AllowUntrustedCertificates { get; set; }
+
#if WINDOWS_UWP
public List Certificates { get; set; }
#else
@@ -23,6 +24,9 @@ namespace MQTTnet.Client.Options
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12;
+ [Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
public Func CertificateValidationCallback { get; set; }
+
+ public Func CertificateValidationHandler { get; set; }
}
}
diff --git a/Source/MQTTnet/Diagnostics/TargetFrameworkProvider.cs b/Source/MQTTnet/Diagnostics/TargetFrameworkProvider.cs
index 7135af6..ea408ca 100644
--- a/Source/MQTTnet/Diagnostics/TargetFrameworkProvider.cs
+++ b/Source/MQTTnet/Diagnostics/TargetFrameworkProvider.cs
@@ -16,6 +16,8 @@
return "netstandard1.3";
#elif NETSTANDARD2_0
return "netstandard2.0";
+#elif NETSTANDARD2_1
+ return "netstandard2.1";
#elif WINDOWS_UWP
return "uap10.0";
#endif
diff --git a/Source/MQTTnet/Implementations/MqttTcpChannel.cs b/Source/MQTTnet/Implementations/MqttTcpChannel.cs
index 021a7d4..756993f 100644
--- a/Source/MQTTnet/Implementations/MqttTcpChannel.cs
+++ b/Source/MQTTnet/Implementations/MqttTcpChannel.cs
@@ -73,7 +73,7 @@ namespace MQTTnet.Implementations
var networkStream = socket.GetStream();
- if (_options.TlsOptions.UseTls)
+ if (_options.TlsOptions?.UseTls == true)
{
var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
try
@@ -181,9 +181,28 @@ namespace MQTTnet.Implementations
bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
- if (_options.TlsOptions.CertificateValidationCallback != null)
+ #region OBSOLETE
+
+ var certificateValidationCallback = _options?.TlsOptions?.CertificateValidationCallback;
+ if (certificateValidationCallback != null)
{
- return _options.TlsOptions.CertificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions);
+ return certificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _clientOptions);
+ }
+
+ #endregion
+
+ var certificateValidationHandler = _options?.TlsOptions?.CertificateValidationHandler;
+ if (certificateValidationHandler != null)
+ {
+ var context = new MqttClientCertificateValidationCallbackContext
+ {
+ Certificate = x509Certificate,
+ Chain = chain,
+ SslPolicyErrors = sslPolicyErrors,
+ ClientOptions = _options
+ };
+
+ return certificateValidationHandler(context);
}
if (sslPolicyErrors == SslPolicyErrors.None)
diff --git a/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs b/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs
index c159b91..e69db2c 100644
--- a/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs
+++ b/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs
@@ -1,21 +1,21 @@
-using System;
+using MQTTnet.Channel;
+using MQTTnet.Client.Options;
+using MQTTnet.Internal;
+using System;
using System.Net;
using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
-using MQTTnet.Channel;
-using MQTTnet.Client.Options;
-using MQTTnet.Internal;
namespace MQTTnet.Implementations
{
- public class MqttWebSocketChannel : Disposable, IMqttChannel
+ public sealed class MqttWebSocketChannel : IMqttChannel
{
- private readonly MqttClientWebSocketOptions _options;
+ readonly MqttClientWebSocketOptions _options;
- private SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1);
- private WebSocket _webSocket;
+ AsyncLock _sendLock = new AsyncLock();
+ WebSocket _webSocket;
public MqttWebSocketChannel(MqttClientWebSocketOptions options)
{
@@ -53,50 +53,20 @@ namespace MQTTnet.Implementations
}
var clientWebSocket = new ClientWebSocket();
-
- if (_options.ProxyOptions != null)
+ try
{
- clientWebSocket.Options.Proxy = CreateProxy();
- }
+ SetupClientWebSocket(clientWebSocket);
- if (_options.RequestHeaders != null)
- {
- foreach (var requestHeader in _options.RequestHeaders)
- {
- clientWebSocket.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
- }
+ await clientWebSocket.ConnectAsync(new Uri(uri), cancellationToken).ConfigureAwait(false);
}
-
- if (_options.SubProtocols != null)
+ catch (Exception)
{
- foreach (var subProtocol in _options.SubProtocols)
- {
- clientWebSocket.Options.AddSubProtocol(subProtocol);
- }
+ // Prevent a memory leak when always creating new instance which will fail while connecting.
+ clientWebSocket.Dispose();
+ throw;
}
- if (_options.CookieContainer != null)
- {
- clientWebSocket.Options.Cookies = _options.CookieContainer;
- }
-
- if (_options.TlsOptions?.UseTls == true && _options.TlsOptions?.Certificates != null)
- {
- clientWebSocket.Options.ClientCertificates = new X509CertificateCollection();
- foreach (var certificate in _options.TlsOptions.Certificates)
- {
-#if WINDOWS_UWP
- clientWebSocket.Options.ClientCertificates.Add(new X509Certificate(certificate));
-#else
- clientWebSocket.Options.ClientCertificates.Add(certificate);
-#endif
-
- }
- }
-
- await clientWebSocket.ConnectAsync(new Uri(uri), cancellationToken).ConfigureAwait(false);
_webSocket = clientWebSocket;
-
IsSecureConnection = uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase);
}
@@ -131,27 +101,87 @@ namespace MQTTnet.Implementations
return;
}
- await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false);
- try
+ using (await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false))
{
await _webSocket.SendAsync(new ArraySegment(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken).ConfigureAwait(false);
}
- finally
- {
- _sendLock?.Release();
- }
}
- protected override void Dispose(bool disposing)
+ public void Dispose()
+ {
+ Cleanup();
+ }
+
+ void SetupClientWebSocket(ClientWebSocket clientWebSocket)
{
- if (disposing)
+
+ if (_options.ProxyOptions != null)
+ {
+ clientWebSocket.Options.Proxy = CreateProxy();
+ }
+
+ if (_options.RequestHeaders != null)
+ {
+ foreach (var requestHeader in _options.RequestHeaders)
+ {
+ clientWebSocket.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
+ }
+ }
+
+ if (_options.SubProtocols != null)
{
- Cleanup();
+ foreach (var subProtocol in _options.SubProtocols)
+ {
+ clientWebSocket.Options.AddSubProtocol(subProtocol);
+ }
}
- base.Dispose(disposing);
+
+ if (_options.CookieContainer != null)
+ {
+ clientWebSocket.Options.Cookies = _options.CookieContainer;
+ }
+
+ if (_options.TlsOptions?.UseTls == true && _options.TlsOptions?.Certificates != null)
+ {
+ clientWebSocket.Options.ClientCertificates = new X509CertificateCollection();
+ foreach (var certificate in _options.TlsOptions.Certificates)
+ {
+#if WINDOWS_UWP
+ clientWebSocket.Options.ClientCertificates.Add(new X509Certificate(certificate));
+#else
+ clientWebSocket.Options.ClientCertificates.Add(certificate);
+#endif
+
+ }
+ }
+
+ var certificateValidationHandler = _options.TlsOptions?.CertificateValidationHandler;
+#if NETSTANDARD2_1
+ if (certificateValidationHandler != null)
+ {
+ clientWebSocket.Options.RemoteCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) =>
+ {
+ // TODO: Find a way to add client options to same callback. Problem is that they have a different type.
+ var context = new MqttClientCertificateValidationCallbackContext
+ {
+ Certificate = certificate,
+ Chain = chain,
+ SslPolicyErrors = sslPolicyErrors,
+ ClientOptions = _options
+ };
+
+ return certificateValidationHandler(context);
+ });
+ }
+#else
+ if (certificateValidationHandler != null)
+ {
+ throw new NotSupportedException("The remote certificate validation callback for Web Sockets is only supported for netstandard 2.1+");
+ }
+#endif
}
- private void Cleanup()
+ void Cleanup()
{
_sendLock?.Dispose();
_sendLock = null;
@@ -169,7 +199,7 @@ namespace MQTTnet.Implementations
}
}
- private IWebProxy CreateProxy()
+ IWebProxy CreateProxy()
{
if (string.IsNullOrEmpty(_options.ProxyOptions?.Address))
{
diff --git a/Source/MQTTnet/Internal/BlockingQueue.cs b/Source/MQTTnet/Internal/BlockingQueue.cs
index 2fa21be..7ca5429 100644
--- a/Source/MQTTnet/Internal/BlockingQueue.cs
+++ b/Source/MQTTnet/Internal/BlockingQueue.cs
@@ -4,11 +4,12 @@ using System.Threading;
namespace MQTTnet.Internal
{
- public class BlockingQueue : Disposable
+ public sealed class BlockingQueue : IDisposable
{
- private readonly object _syncRoot = new object();
- private readonly LinkedList _items = new LinkedList();
- private readonly ManualResetEventSlim _gate = new ManualResetEventSlim(false);
+ readonly object _syncRoot = new object();
+ readonly LinkedList _items = new LinkedList();
+
+ ManualResetEventSlim _gate = new ManualResetEventSlim(false);
public int Count
{
@@ -28,13 +29,13 @@ namespace MQTTnet.Internal
lock (_syncRoot)
{
_items.AddLast(item);
- _gate.Set();
+ _gate?.Set();
}
}
- public TItem Dequeue(CancellationToken cancellationToken = default(CancellationToken))
+ public TItem Dequeue(CancellationToken cancellationToken = default)
{
- while (true)
+ while (!cancellationToken.IsCancellationRequested)
{
lock (_syncRoot)
{
@@ -48,17 +49,19 @@ namespace MQTTnet.Internal
if (_items.Count == 0)
{
- _gate.Reset();
+ _gate?.Reset();
}
}
- _gate.Wait(cancellationToken);
+ _gate?.Wait(cancellationToken);
}
+
+ throw new OperationCanceledException();
}
-
- public TItem PeekAndWait(CancellationToken cancellationToken = default(CancellationToken))
+
+ public TItem PeekAndWait(CancellationToken cancellationToken = default)
{
- while (true)
+ while (!cancellationToken.IsCancellationRequested)
{
lock (_syncRoot)
{
@@ -69,12 +72,14 @@ namespace MQTTnet.Internal
if (_items.Count == 0)
{
- _gate.Reset();
+ _gate?.Reset();
}
}
- _gate.Wait(cancellationToken);
+ _gate?.Wait(cancellationToken);
}
+
+ throw new OperationCanceledException();
}
public void RemoveFirst(Predicate match)
@@ -109,13 +114,10 @@ namespace MQTTnet.Internal
}
}
- protected override void Dispose(bool disposing)
+ public void Dispose()
{
- if (disposing)
- {
- _gate.Dispose();
- }
- base.Dispose(disposing);
+ _gate?.Dispose();
+ _gate = null;
}
}
}
diff --git a/Source/MQTTnet/MQTTnet.csproj b/Source/MQTTnet/MQTTnet.csproj
index a08f843..896f7a8 100644
--- a/Source/MQTTnet/MQTTnet.csproj
+++ b/Source/MQTTnet/MQTTnet.csproj
@@ -1,7 +1,7 @@
- netstandard1.3;netstandard2.0
+ netstandard1.3;netstandard2.0;netstandard2.1
$(TargetFrameworks);net452;net461
$(TargetFrameworks);uap10.0
MQTTnet
diff --git a/Tests/MQTTnet.Core.Tests/BlockingQueue_Tests.cs b/Tests/MQTTnet.Core.Tests/BlockingQueue_Tests.cs
index da4069a..01e786f 100644
--- a/Tests/MQTTnet.Core.Tests/BlockingQueue_Tests.cs
+++ b/Tests/MQTTnet.Core.Tests/BlockingQueue_Tests.cs
@@ -1,7 +1,8 @@
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Internal;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
namespace MQTTnet.Tests
{
@@ -33,7 +34,7 @@ namespace MQTTnet.Tests
Assert.AreEqual("a", queue.RemoveFirst());
Assert.AreEqual("b", queue.RemoveFirst());
-
+
Assert.AreEqual(1, queue.Count);
Assert.AreEqual("c", queue.Dequeue());
@@ -81,7 +82,7 @@ namespace MQTTnet.Tests
}
[TestMethod]
- public void Wait_For_Times()
+ public void Wait_For_Items()
{
var number = 0;
@@ -104,5 +105,21 @@ namespace MQTTnet.Tests
Interlocked.Increment(ref number);
}
}
+
+ [TestMethod]
+ [ExpectedException(typeof(OperationCanceledException))]
+ public void Use_Disposed_Queue()
+ {
+ var queue = new BlockingQueue();
+#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
+ Task.Run(() =>
+ {
+ Thread.Sleep(1000);
+ queue.Dispose();
+ });
+#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
+
+ queue.Dequeue(new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token);
+ }
}
-}
+}
\ No newline at end of file
diff --git a/Tests/MQTTnet.Core.Tests/MqttClient_Tests.cs b/Tests/MQTTnet.Core.Tests/MqttClient_Tests.cs
index 5c99031..b7c8a47 100644
--- a/Tests/MQTTnet.Core.Tests/MqttClient_Tests.cs
+++ b/Tests/MQTTnet.Core.Tests/MqttClient_Tests.cs
@@ -23,6 +23,18 @@ namespace MQTTnet.Tests
{
public TestContext TestContext { get; set; }
+ [TestMethod]
+ public async Task Send_Manual_Ping()
+ {
+ using (var testEnvironment = new TestEnvironment(TestContext))
+ {
+ await testEnvironment.StartServerAsync();
+ var client = await testEnvironment.ConnectClientAsync();
+
+ await client.PingAsync(CancellationToken.None);
+ }
+ }
+
[TestMethod]
public async Task Send_Reply_In_Message_Handler_For_Same_Client()
{
diff --git a/Tests/MQTTnet.TestApp.NetCore/Program.cs b/Tests/MQTTnet.TestApp.NetCore/Program.cs
index 9b2cbeb..2dc2e0e 100644
--- a/Tests/MQTTnet.TestApp.NetCore/Program.cs
+++ b/Tests/MQTTnet.TestApp.NetCore/Program.cs
@@ -1,15 +1,13 @@
-using System;
+using MQTTnet.Client.Options;
+using MQTTnet.Diagnostics;
+using MQTTnet.Server;
+using Newtonsoft.Json;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Security;
-using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
-using MQTTnet.Client;
-using MQTTnet.Client.Options;
-using MQTTnet.Diagnostics;
-using MQTTnet.Server;
-using Newtonsoft.Json;
namespace MQTTnet.TestApp.NetCore
{
@@ -129,10 +127,15 @@ namespace MQTTnet.TestApp.NetCore
var options = new MqttClientOptionsBuilder()
.WithTls(new MqttClientOptionsBuilderTlsParameters
{
- CertificateValidationCallback = (X509Certificate x, X509Chain y, SslPolicyErrors z, IMqttClientOptions o) =>
+ CertificateValidationHandler = context =>
{
- // TODO: Check conditions of certificate by using above parameters.
- return true;
+ // TODO: Check conditions of certificate by using above context.
+ if (context.SslPolicyErrors == SslPolicyErrors.None)
+ {
+ return true;
+ }
+
+ return false;
}
})
.Build();