1595 object disposed exception in unit test handle parallel connection attempts (#1610)

This commit is contained in:
Christian
2022-12-05 19:22:18 +01:00
committed by GitHub
parent bb106f8c6e
commit c17a350796
34 changed files with 802 additions and 484 deletions
+6 -1
View File
@@ -243,6 +243,11 @@ See the LICENSE file in the project root for more information.</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=CONNACK/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=CONNACK/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGRESP/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=PINGRESP/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBACK/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBCOMP/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=PUBCOMP/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBREC/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBREL/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=SUBACK/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Tnet/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Tnet/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=UNSUBACK/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -18,7 +18,7 @@ using MQTTnet.Exceptions;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using MQTTnet.Tests.Extensions; using MQTTnet.Tests.Helpers;
namespace MQTTnet.AspNetCore.Tests namespace MQTTnet.AspNetCore.Tests
{ {
@@ -35,7 +35,7 @@ namespace MQTTnet.AspNetCore.Tests
connection.Transport = pipe; connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection); var ctx = new MqttConnectionContext(serializer, connection);
await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, new byte[0])); await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, Array.Empty<byte>()));
await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(CancellationToken.None)); await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
@@ -19,8 +19,8 @@ namespace MQTTnet.Benchmarks
[MemoryDiagnoser] [MemoryDiagnoser]
public class ReaderExtensionsBenchmark public class ReaderExtensionsBenchmark
{ {
private MqttPacketFormatterAdapter mqttPacketFormatter; MqttPacketFormatterAdapter mqttPacketFormatter;
private MemoryStream stream; MemoryStream stream;
[GlobalSetup] [GlobalSetup]
public void GlobalSetup() public void GlobalSetup()
@@ -31,8 +31,7 @@ namespace MQTTnet.Benchmarks
.WithPayload(new byte[10 * 1024]) .WithPayload(new byte[10 * 1024])
.Build(); .Build();
var packetFactories = new MqttPacketFactories(); var packet = MqttPacketFactories.Publish.Create(mqttMessage);
var packet = packetFactories.Publish.Create(mqttMessage);
var buffer = mqttPacketFormatter.Encode(packet); var buffer = mqttPacketFormatter.Encode(packet);
stream = new MemoryStream(); stream = new MemoryStream();
@@ -34,6 +34,7 @@
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers> <DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>1591</NoWarn> <NoWarn>1591</NoWarn>
<LangVersion>7.3</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'"> <PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
@@ -11,10 +11,12 @@ namespace MQTTnet.Extensions.Rpc
{ {
public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory factory, IMqttClient mqttClient) public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory factory, IMqttClient mqttClient)
{ {
return factory.CreateMqttRpcClient(mqttClient, new MqttRpcClientOptions return factory.CreateMqttRpcClient(
{ mqttClient,
TopicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy() new MqttRpcClientOptions
}); {
TopicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy()
});
} }
public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory _, IMqttClient mqttClient, MqttRpcClientOptions rpcClientOptions) public static IMqttRpcClient CreateMqttRpcClient(this MqttFactory _, IMqttClient mqttClient, MqttRpcClientOptions rpcClientOptions)
@@ -17,8 +17,9 @@ namespace MQTTnet.Extensions.Rpc
{ {
readonly IMqttClient _mqttClient; readonly IMqttClient _mqttClient;
readonly MqttRpcClientOptions _options; readonly MqttRpcClientOptions _options;
readonly ConcurrentDictionary<string, AsyncTaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, AsyncTaskCompletionSource<byte[]>>(); readonly ConcurrentDictionary<string, AsyncTaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, AsyncTaskCompletionSource<byte[]>>();
public MqttRpcClient(IMqttClient mqttClient, MqttRpcClientOptions options) public MqttRpcClient(IMqttClient mqttClient, MqttRpcClientOptions options)
{ {
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient)); _mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
@@ -66,14 +67,8 @@ namespace MQTTnet.Extensions.Rpc
throw new ArgumentNullException(nameof(methodName)); throw new ArgumentNullException(nameof(methodName));
} }
var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics( var context = new TopicGenerationContext(_mqttClient, _options, methodName, qualityOfServiceLevel);
new TopicGenerationContext var topicNames = _options.TopicGenerationStrategy.CreateRpcTopics(context);
{
MethodName = methodName,
QualityOfServiceLevel = qualityOfServiceLevel,
MqttClient = _mqttClient,
Options = _options
});
var requestTopic = topicNames.RequestTopic; var requestTopic = topicNames.RequestTopic;
var responseTopic = topicNames.ResponseTopic; var responseTopic = topicNames.ResponseTopic;
@@ -10,13 +10,6 @@ namespace MQTTnet.Extensions.Rpc
{ {
IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy(); IMqttRpcClientTopicGenerationStrategy _topicGenerationStrategy = new DefaultMqttRpcClientTopicGenerationStrategy();
public MqttRpcClientOptionsBuilder WithTopicGenerationStrategy(IMqttRpcClientTopicGenerationStrategy value)
{
_topicGenerationStrategy = value ?? throw new ArgumentNullException(nameof(value));
return this;
}
public MqttRpcClientOptions Build() public MqttRpcClientOptions Build()
{ {
return new MqttRpcClientOptions return new MqttRpcClientOptions
@@ -24,5 +17,12 @@ namespace MQTTnet.Extensions.Rpc
TopicGenerationStrategy = _topicGenerationStrategy TopicGenerationStrategy = _topicGenerationStrategy
}; };
} }
public MqttRpcClientOptionsBuilder WithTopicGenerationStrategy(IMqttRpcClientTopicGenerationStrategy value)
{
_topicGenerationStrategy = value ?? throw new ArgumentNullException(nameof(value));
return this;
}
} }
} }
@@ -10,6 +10,11 @@ namespace MQTTnet.Extensions.Rpc
{ {
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context) public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
{ {
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.MethodName.Contains("/") || context.MethodName.Contains("+") || context.MethodName.Contains("#")) if (context.MethodName.Contains("/") || context.MethodName.Contains("+") || context.MethodName.Contains("#"))
{ {
throw new ArgumentException("The method name cannot contain /, + or #."); throw new ArgumentException("The method name cannot contain /, + or #.");
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Protocol; using MQTTnet.Protocol;
@@ -9,20 +10,29 @@ namespace MQTTnet.Extensions.Rpc
{ {
public sealed class TopicGenerationContext public sealed class TopicGenerationContext
{ {
public string MethodName { get; set; } public TopicGenerationContext(IMqttClient mqttClient, MqttRpcClientOptions options, string methodName, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
MethodName = methodName ?? throw new ArgumentNullException(nameof(methodName));
QualityOfServiceLevel = qualityOfServiceLevel;
MqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
Options = options ?? throw new ArgumentNullException(nameof(options));
}
public string MethodName { get; }
public IMqttClient MqttClient { get; }
public MqttRpcClientOptions Options { get; }
/// <summary> /// <summary>
/// Gets or sets the quality of service level. /// Gets or sets the quality of service level.
/// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message that defines the guarantee of delivery for a specific message. /// The Quality of Service (QoS) level is an agreement between the sender of a message and the receiver of a message
/// There are 3 QoS levels in MQTT: /// that defines the guarantee of delivery for a specific message.
/// - At most once (0): Message gets delivered no time, once or multiple times. /// There are 3 QoS levels in MQTT:
/// - At least once (1): Message gets delivered at least once (one time or more often). /// - At most once (0): Message gets delivered no time, once or multiple times.
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once). /// - At least once (1): Message gets delivered at least once (one time or more often).
/// - Exactly once (2): Message gets delivered exactly once (It's ensured that the message only comes once).
/// </summary> /// </summary>
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } public MqttQualityOfServiceLevel QualityOfServiceLevel { get; }
public IMqttClient MqttClient { get; set; }
public MqttRpcClientOptions Options { get; set; }
} }
} }
@@ -34,6 +34,7 @@
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers> <DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>1591</NoWarn> <NoWarn>1591</NoWarn>
<LangVersion>7.3</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'"> <PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
@@ -10,9 +10,12 @@ namespace MQTTnet.Extensions.WebSocket4Net
{ {
public static MqttFactory UseWebSocket4Net(this MqttFactory mqttFactory) public static MqttFactory UseWebSocket4Net(this MqttFactory mqttFactory)
{ {
if (mqttFactory == null) throw new ArgumentNullException(nameof(mqttFactory)); if (mqttFactory == null)
{
throw new ArgumentNullException(nameof(mqttFactory));
}
return mqttFactory.UseClientAdapterFactory(new WebSocket4NetMqttClientAdapterFactory()); return mqttFactory.UseClientAdapterFactory(new WebSocket4NetMqttClientAdapterFactory());
} }
} }
} }
@@ -5,6 +5,7 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Security.Authentication; using System.Security.Authentication;
@@ -17,14 +18,14 @@ using MQTTnet.Exceptions;
using MQTTnet.Internal; using MQTTnet.Internal;
using SuperSocket.ClientEngine; using SuperSocket.ClientEngine;
using WebSocket4Net; using WebSocket4Net;
using DataReceivedEventArgs = WebSocket4Net.DataReceivedEventArgs;
namespace MQTTnet.Extensions.WebSocket4Net namespace MQTTnet.Extensions.WebSocket4Net
{ {
public sealed class WebSocket4NetMqttChannel : IMqttChannel public sealed class WebSocket4NetMqttChannel : IMqttChannel
{ {
readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
readonly MqttClientOptions _clientOptions; readonly MqttClientOptions _clientOptions;
readonly BlockingCollection<byte> _receiveBuffer = new BlockingCollection<byte>();
readonly MqttClientWebSocketOptions _webSocketOptions; readonly MqttClientWebSocketOptions _webSocketOptions;
WebSocket _webSocket; WebSocket _webSocket;
@@ -35,13 +36,13 @@ namespace MQTTnet.Extensions.WebSocket4Net
_webSocketOptions = webSocketOptions ?? throw new ArgumentNullException(nameof(webSocketOptions)); _webSocketOptions = webSocketOptions ?? throw new ArgumentNullException(nameof(webSocketOptions));
} }
public X509Certificate2 ClientCertificate { get; }
public string Endpoint => _webSocketOptions.Uri; public string Endpoint => _webSocketOptions.Uri;
public bool IsSecureConnection { get; private set; } public bool IsSecureConnection { get; private set; }
public X509Certificate2 ClientCertificate { get; } public Task ConnectAsync(CancellationToken cancellationToken)
public async Task ConnectAsync(CancellationToken cancellationToken)
{ {
var uri = _webSocketOptions.Uri; var uri = _webSocketOptions.Uri;
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase)) if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase))
@@ -55,11 +56,13 @@ namespace MQTTnet.Extensions.WebSocket4Net
uri = "wss://" + uri; uri = "wss://" + uri;
} }
} }
IsSecureConnection = uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase);
#if NET48 || NETCOREAPP3_1 || NET5 || NET6 #if NET48 || NETCOREAPP3_0_OR_GREATER
var sslProtocols = _webSocketOptions?.TlsOptions?.SslProtocol ?? SslProtocols.Tls12 | SslProtocols.Tls13; var sslProtocols = _webSocketOptions.TlsOptions?.SslProtocol ?? SslProtocols.Tls12 | SslProtocols.Tls13;
#else #else
var sslProtocols = _webSocketOptions?.TlsOptions?.SslProtocol ?? SslProtocols.Tls12 | (SslProtocols)0x00003000 /*Tls13*/; var sslProtocols = _webSocketOptions.TlsOptions?.SslProtocol ?? SslProtocols.Tls12 | (SslProtocols)0x00003000 /*Tls13*/;
#endif #endif
var subProtocol = _webSocketOptions.SubProtocols.FirstOrDefault() ?? string.Empty; var subProtocol = _webSocketOptions.SubProtocols.FirstOrDefault() ?? string.Empty;
@@ -90,7 +93,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
var receiveBufferSize = 0; var receiveBufferSize = 0;
var certificates = new X509CertificateCollection(); var certificates = new X509CertificateCollection();
if (_webSocketOptions?.TlsOptions?.Certificates != null) if (_webSocketOptions.TlsOptions?.Certificates != null)
{ {
foreach (var certificate in _webSocketOptions.TlsOptions.Certificates) foreach (var certificate in _webSocketOptions.TlsOptions.Certificates)
{ {
@@ -107,15 +110,13 @@ namespace MQTTnet.Extensions.WebSocket4Net
NoDelay = true, NoDelay = true,
Security = Security =
{ {
AllowUnstrustedCertificate = _webSocketOptions?.TlsOptions?.AllowUntrustedCertificates == true, AllowUnstrustedCertificate = _webSocketOptions.TlsOptions?.AllowUntrustedCertificates == true,
AllowCertificateChainErrors = _webSocketOptions?.TlsOptions?.IgnoreCertificateChainErrors == true, AllowCertificateChainErrors = _webSocketOptions.TlsOptions?.IgnoreCertificateChainErrors == true,
Certificates = certificates Certificates = certificates
} }
}; };
await ConnectInternalAsync(cancellationToken).ConfigureAwait(false); return ConnectInternalAsync(cancellationToken);
IsSecureConnection = uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase);
} }
public Task DisconnectAsync(CancellationToken cancellationToken) public Task DisconnectAsync(CancellationToken cancellationToken)
@@ -124,10 +125,23 @@ namespace MQTTnet.Extensions.WebSocket4Net
{ {
_webSocket.Close(); _webSocket.Close();
} }
return CompletedTask.Instance; return CompletedTask.Instance;
} }
public void Dispose()
{
if (_webSocket == null)
{
return;
}
_webSocket.DataReceived -= OnDataReceived;
_webSocket.Error -= OnError;
_webSocket.Dispose();
_webSocket = null;
}
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{ {
var readBytes = 0; var readBytes = 0;
@@ -161,75 +175,55 @@ namespace MQTTnet.Extensions.WebSocket4Net
return CompletedTask.Instance; return CompletedTask.Instance;
} }
public void Dispose()
{
if (_webSocket == null)
{
return;
}
_webSocket.DataReceived -= OnDataReceived;
_webSocket.Error -= OnError;
_webSocket.Dispose();
_webSocket = null;
}
static void OnError(object sender, ErrorEventArgs e)
{
System.Diagnostics.Debug.Write(e.Exception.ToString());
}
void OnDataReceived(object sender, DataReceivedEventArgs e)
{
foreach (var @byte in e.Data)
{
_receiveBuffer.Add(@byte);
}
}
async Task ConnectInternalAsync(CancellationToken cancellationToken) async Task ConnectInternalAsync(CancellationToken cancellationToken)
{ {
_webSocket.Error += OnError; _webSocket.Error += OnError;
_webSocket.DataReceived += OnDataReceived; _webSocket.DataReceived += OnDataReceived;
var taskCompletionSource = new TaskCompletionSource<Exception>(); var promise = new AsyncTaskCompletionSource<bool>();
void ErrorHandler(object sender, ErrorEventArgs e) void ErrorHandler(object sender, ErrorEventArgs e)
{ {
taskCompletionSource.TrySetResult(e.Exception); promise.TrySetException(e.Exception);
} }
void SuccessHandler(object sender, EventArgs e) void OpenedHandler(object sender, EventArgs e)
{ {
taskCompletionSource.TrySetResult(null); promise.TrySetResult(true);
}
void ClosedHandler(object sender, EventArgs e)
{
promise.TrySetException(new MqttCommunicationException("Connection closed."));
} }
try try
{ {
_webSocket.Opened += SuccessHandler; _webSocket.Opened += OpenedHandler;
_webSocket.Closed += ClosedHandler;
_webSocket.Error += ErrorHandler; _webSocket.Error += ErrorHandler;
#pragma warning disable AsyncFixer02 // Long-running or blocking operations inside an async method using (var timeout = new CancellationTokenSource(_clientOptions.Timeout))
_webSocket.Open(); using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken))
#pragma warning restore AsyncFixer02 // Long-running or blocking operations inside an async method
using (var timeoutCts = new CancellationTokenSource(_clientOptions.Timeout))
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken))
{ {
using (linkedCts.Token.Register(() => taskCompletionSource.TrySetCanceled())) using (linkedCts.Token.Register(() => promise.TrySetCanceled()))
{ {
try try
{ {
await taskCompletionSource.Task.ConfigureAwait(false); // OpenAsync does basically the same but without support for a cancellation token.
// So we do it on our own!
_webSocket.Open();
await promise.Task.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
var timeoutReached = timeoutCts.IsCancellationRequested && !cancellationToken.IsCancellationRequested; var timeoutReached = timeout.IsCancellationRequested && !cancellationToken.IsCancellationRequested;
if (timeoutReached) if (timeoutReached)
{ {
throw new MqttCommunicationTimedOutException(exception); throw new MqttCommunicationTimedOutException(exception);
} }
if (exception is AuthenticationException authenticationException) if (exception is AuthenticationException authenticationException)
{ {
throw new MqttCommunicationException(authenticationException.InnerException); throw new MqttCommunicationException(authenticationException.InnerException);
@@ -251,9 +245,23 @@ namespace MQTTnet.Extensions.WebSocket4Net
} }
finally finally
{ {
_webSocket.Opened -= SuccessHandler; _webSocket.Opened -= OpenedHandler;
_webSocket.Closed -= ClosedHandler;
_webSocket.Error -= ErrorHandler; _webSocket.Error -= ErrorHandler;
} }
} }
void OnDataReceived(object sender, DataReceivedEventArgs e)
{
foreach (var @byte in e.Data)
{
_receiveBuffer.Add(@byte);
}
}
static void OnError(object sender, ErrorEventArgs e)
{
Debug.Write(e.Exception.ToString());
}
} }
} }
@@ -2,12 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using MQTTnet.Adapter;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
using System; using System;
using MQTTnet.Adapter;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Diagnostics; using MQTTnet.Diagnostics;
using MQTTnet.Formatter;
using MQTTnet.Implementations;
namespace MQTTnet.Extensions.WebSocket4Net namespace MQTTnet.Extensions.WebSocket4Net
{ {
@@ -15,7 +15,10 @@ namespace MQTTnet.Extensions.WebSocket4Net
{ {
public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger) public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
{ {
if (options == null) throw new ArgumentNullException(nameof(options)); if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
switch (options.ChannelOptions) switch (options.ChannelOptions)
{ {
@@ -23,7 +26,7 @@ namespace MQTTnet.Extensions.WebSocket4Net
{ {
return new MqttChannelAdapter( return new MqttChannelAdapter(
new MqttTcpChannel(options), new MqttTcpChannel(options),
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4096, 65535)), new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
packetInspector, packetInspector,
logger); logger);
} }
@@ -32,16 +35,16 @@ namespace MQTTnet.Extensions.WebSocket4Net
{ {
return new MqttChannelAdapter( return new MqttChannelAdapter(
new WebSocket4NetMqttChannel(options, webSocketOptions), new WebSocket4NetMqttChannel(options, webSocketOptions),
new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4068, 65535)), new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(options.WriterBufferSize, options.WriterBufferSizeMax)),
packetInspector, packetInspector,
logger); logger);
} }
default: default:
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
} }
} }
} }
} }
+105 -72
View File
@@ -2,11 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using MQTTnet.Client;
using System; using System;
using System.Security.Authentication; using System.Security.Authentication;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MQTTnet.Client;
using MQTTnet.Extensions.WebSocket4Net;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Internal; using MQTTnet.Internal;
using MQTTnet.Protocol; using MQTTnet.Protocol;
@@ -17,10 +18,6 @@ namespace MQTTnet.TestApp
{ {
public static async Task RunAsync() public static async Task RunAsync()
{ {
// MqttNetConsoleLogger.ForwardToConsole();
// For most of these connections to work, set output target to Net5.0.
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
// TLS13 is only available in Net5.0 // TLS13 is only available in Net5.0
var unsafeTls13 = new MqttClientOptionsBuilderTlsParameters var unsafeTls13 = new MqttClientOptionsBuilderTlsParameters
@@ -31,7 +28,6 @@ namespace MQTTnet.TestApp
CertificateValidationHandler = w => true CertificateValidationHandler = w => true
}; };
#endif #endif
// Also defining TLS12 for servers that don't seem no to support TLS13. // Also defining TLS12 for servers that don't seem no to support TLS13.
var unsafeTls12 = new MqttClientOptionsBuilderTlsParameters var unsafeTls12 = new MqttClientOptionsBuilderTlsParameters
{ {
@@ -42,33 +38,37 @@ namespace MQTTnet.TestApp
}; };
// mqtt.eclipseprojects.io // mqtt.eclipseprojects.io
await ExecuteTestAsync("mqtt.eclipseprojects.io TCP", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithTcpServer("mqtt.eclipseprojects.io", 1883) "mqtt.eclipseprojects.io TCP",
.WithProtocolVersion(MqttProtocolVersion.V311).Build()); new MqttClientOptionsBuilder().WithTcpServer("mqtt.eclipseprojects.io", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
await ExecuteTestAsync("mqtt.eclipseprojects.io WS", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:80/mqtt") "mqtt.eclipseprojects.io WS",
.WithProtocolVersion(MqttProtocolVersion.V311).Build()); new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:80/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
await ExecuteTestAsync("mqtt.eclipseprojects.io WS TLS13", await ExecuteTestAsync("mqtt.eclipseprojects.io WS TLS13",
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:443/mqtt") new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:443/mqtt")
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build()); .WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
await ExecuteTestAsync("mqtt.eclipseprojects.io WS TLS13 (WebSocket4Net)",
new MqttClientOptionsBuilder().WithWebSocketServer("mqtt.eclipseprojects.io:443/mqtt")
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build(),
true);
#endif #endif
// test.mosquitto.org // test.mosquitto.org
await ExecuteTestAsync("test.mosquitto.org TCP", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1883) "test.mosquitto.org TCP",
.WithProtocolVersion(MqttProtocolVersion.V311).Build()); new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
await ExecuteTestAsync("test.mosquitto.org TCP - Authenticated", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1884) "test.mosquitto.org TCP - Authenticated",
.WithCredentials("rw", "readwrite") new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 1884).WithCredentials("rw", "readwrite").WithProtocolVersion(MqttProtocolVersion.V311).Build());
.WithProtocolVersion(MqttProtocolVersion.V311).Build());
await ExecuteTestAsync("test.mosquitto.org TCP TLS12", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883) "test.mosquitto.org TCP TLS12",
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build()); new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
await ExecuteTestAsync("test.mosquitto.org TCP TLS13", await ExecuteTestAsync("test.mosquitto.org TCP TLS13",
@@ -76,27 +76,40 @@ namespace MQTTnet.TestApp
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build()); .WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
#endif #endif
await ExecuteTestAsync("test.mosquitto.org TCP TLS12 - Authenticated", await ExecuteTestAsync(
"test.mosquitto.org TCP TLS12 - Authenticated",
new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8885) new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8885)
.WithCredentials("rw", "readwrite") .WithCredentials("rw", "readwrite")
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build()); .WithProtocolVersion(MqttProtocolVersion.V311)
.WithTls(unsafeTls12)
.Build());
await ExecuteTestAsync("test.mosquitto.org WS", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8080/mqtt") "test.mosquitto.org WS",
.WithProtocolVersion(MqttProtocolVersion.V311).Build()); new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8080/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
await ExecuteTestAsync("test.mosquitto.org WS TLS12", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt") "test.mosquitto.org WS (WebSocket4Net)",
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build()); new MqttClientOptionsBuilder().WithWebSocketServer("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());
// await ExecuteTestAsync(
// "test.mosquitto.org WS TLS12 (WebSocket4Net)",
// new MqttClientOptionsBuilder().WithWebSocketServer("test.mosquitto.org:8081/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build(),
// true);
// broker.emqx.io // broker.emqx.io
await ExecuteTestAsync("broker.emqx.io TCP", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 1883) "broker.emqx.io TCP",
.WithProtocolVersion(MqttProtocolVersion.V311).Build()); new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
await ExecuteTestAsync("broker.emqx.io TCP TLS12", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883) "broker.emqx.io TCP TLS12",
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build()); new MqttClientOptionsBuilder().WithTcpServer("broker.emqx.io", 8883).WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build());
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
await ExecuteTestAsync("broker.emqx.io TCP TLS13", await ExecuteTestAsync("broker.emqx.io TCP TLS13",
@@ -104,23 +117,37 @@ namespace MQTTnet.TestApp
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build()); .WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls13).Build());
#endif #endif
await ExecuteTestAsync("broker.emqx.io WS", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8083/mqtt") "broker.emqx.io WS",
.WithProtocolVersion(MqttProtocolVersion.V311).Build()); new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8083/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).Build());
await ExecuteTestAsync("broker.emqx.io WS TLS12", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8084/mqtt") "broker.emqx.io WS (WebSocket4Net)",
.WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build()); new MqttClientOptionsBuilder().WithWebSocketServer("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());
await ExecuteTestAsync(
"broker.emqx.io WS TLS12 (WebSocket4Net)",
new MqttClientOptionsBuilder().WithWebSocketServer("broker.emqx.io:8084/mqtt").WithProtocolVersion(MqttProtocolVersion.V311).WithTls(unsafeTls12).Build(),
true);
// broker.hivemq.com // broker.hivemq.com
await ExecuteTestAsync("broker.hivemq.com TCP", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com", 1883) "broker.hivemq.com TCP",
.WithProtocolVersion(MqttProtocolVersion.V311).Build()); new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com", 1883).WithProtocolVersion(MqttProtocolVersion.V311).Build());
await ExecuteTestAsync("broker.hivemq.com WS", await ExecuteTestAsync(
new MqttClientOptionsBuilder().WithWebSocketServer("broker.hivemq.com:8000/mqtt") "broker.hivemq.com WS",
.WithProtocolVersion(MqttProtocolVersion.V311).Build()); new MqttClientOptionsBuilder().WithWebSocketServer("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(),
true);
// mqtt.swifitch.cz: Does not seem to operate any more // mqtt.swifitch.cz: Does not seem to operate any more
@@ -130,36 +157,43 @@ namespace MQTTnet.TestApp
Console.ReadLine(); Console.ReadLine();
} }
static async Task ExecuteTestAsync(string name, MqttClientOptions options) static async Task ExecuteTestAsync(string name, MqttClientOptions options, bool useWebSocket4Net = false)
{ {
try try
{ {
Write("Testing '" + name + "'... ", ConsoleColor.Gray); Write("Testing '" + name + "'... ", ConsoleColor.Gray);
var factory = new MqttFactory(); var factory = new MqttFactory();
//factory.UseWebSocket4Net(); if (useWebSocket4Net)
var client = factory.CreateMqttClient();
var topic = Guid.NewGuid().ToString();
MqttApplicationMessage receivedMessage = null;
client.ApplicationMessageReceivedAsync += e =>
{ {
receivedMessage = e.ApplicationMessage; factory.UseWebSocket4Net();
return CompletedTask.Instance;
};
await client.ConnectAsync(options);
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce);
await client.PublishStringAsync(topic, "Hello_World", MqttQualityOfServiceLevel.AtLeastOnce);
SpinWait.SpinUntil(() => receivedMessage != null, 5000);
if (receivedMessage?.Topic != topic || receivedMessage?.ConvertPayloadToString() != "Hello_World")
{
throw new Exception("Message invalid.");
} }
await client.UnsubscribeAsync(topic); using (var client = factory.CreateMqttClient())
await client.DisconnectAsync(); {
MqttApplicationMessage receivedMessage = null;
client.ApplicationMessageReceivedAsync += e =>
{
receivedMessage = e.ApplicationMessage;
return CompletedTask.Instance;
};
await client.ConnectAsync(options).ConfigureAwait(false);
var topic = Guid.NewGuid().ToString();
await client.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce).ConfigureAwait(false);
await client.PublishStringAsync(topic, "Hello_World", MqttQualityOfServiceLevel.AtLeastOnce).ConfigureAwait(false);
SpinWait.SpinUntil(() => receivedMessage != null, 5000);
if (receivedMessage?.Topic != topic || receivedMessage?.ConvertPayloadToString() != "Hello_World")
{
throw new Exception("Message invalid.");
}
await client.UnsubscribeAsync(topic).ConfigureAwait(false);
await client.DisconnectAsync().ConfigureAwait(false);
}
Write("[OK]\n", ConsoleColor.Green); Write("[OK]\n", ConsoleColor.Green);
} }
@@ -174,6 +208,5 @@ namespace MQTTnet.TestApp
Console.ForegroundColor = color; Console.ForegroundColor = color;
Console.Write(message); Console.Write(message);
} }
} }
} }
@@ -6,68 +6,93 @@ using System;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Tests.Mockups;
using MQTTnet.Client; using MQTTnet.Client;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Extensions.Rpc; using MQTTnet.Extensions.Rpc;
using MQTTnet.Protocol;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Implementations;
using MQTTnet.Internal; using MQTTnet.Internal;
using MQTTnet.Protocol;
using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests namespace MQTTnet.Tests.Extensions
{ {
[TestClass] [TestClass]
public sealed class RPC_Tests : BaseTestClass public sealed class Rpc_Tests : BaseTestClass
{ {
[TestMethod]
public async Task Execute_Success_MQTT_V5_Mixed_Clients()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var responseSender = await testEnvironment.ConnectClient();
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping");
responseSender.ApplicationMessageReceivedAsync += async e =>
{
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
await responseSender.PublishStringAsync(e.ApplicationMessage.Topic + "/response", "pong");
};
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
using (var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build()))
{
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
}
}
}
[TestMethod] [TestMethod]
public Task Execute_Success_With_QoS_0() public Task Execute_Success_With_QoS_0()
{ {
return Execute_Success(MqttQualityOfServiceLevel.AtMostOnce, MqttProtocolVersion.V311); return Execute_Success(MqttQualityOfServiceLevel.AtMostOnce, MqttProtocolVersion.V311);
} }
[TestMethod]
public Task Execute_Success_With_QoS_1()
{
return Execute_Success(MqttQualityOfServiceLevel.AtLeastOnce, MqttProtocolVersion.V311);
}
[TestMethod]
public Task Execute_Success_With_QoS_2()
{
return Execute_Success(MqttQualityOfServiceLevel.ExactlyOnce, MqttProtocolVersion.V311);
}
[TestMethod] [TestMethod]
public Task Execute_Success_With_QoS_0_MQTT_V5() public Task Execute_Success_With_QoS_0_MQTT_V5()
{ {
return Execute_Success(MqttQualityOfServiceLevel.AtMostOnce, MqttProtocolVersion.V500); return Execute_Success(MqttQualityOfServiceLevel.AtMostOnce, MqttProtocolVersion.V500);
} }
[TestMethod]
public Task Execute_Success_With_QoS_1_MQTT_V5()
{
return Execute_Success(MqttQualityOfServiceLevel.AtLeastOnce, MqttProtocolVersion.V500);
}
[TestMethod]
public Task Execute_Success_With_QoS_2_MQTT_V5()
{
return Execute_Success(MqttQualityOfServiceLevel.ExactlyOnce, MqttProtocolVersion.V500);
}
[TestMethod] [TestMethod]
public Task Execute_Success_With_QoS_0_MQTT_V5_Use_ResponseTopic() public Task Execute_Success_With_QoS_0_MQTT_V5_Use_ResponseTopic()
{ {
return Execute_Success_MQTT_V5(MqttQualityOfServiceLevel.AtMostOnce); return Execute_Success_MQTT_V5(MqttQualityOfServiceLevel.AtMostOnce);
} }
[TestMethod]
public Task Execute_Success_With_QoS_1()
{
return Execute_Success(MqttQualityOfServiceLevel.AtLeastOnce, MqttProtocolVersion.V311);
}
[TestMethod]
public Task Execute_Success_With_QoS_1_MQTT_V5()
{
return Execute_Success(MqttQualityOfServiceLevel.AtLeastOnce, MqttProtocolVersion.V500);
}
[TestMethod] [TestMethod]
public Task Execute_Success_With_QoS_1_MQTT_V5_Use_ResponseTopic() public Task Execute_Success_With_QoS_1_MQTT_V5_Use_ResponseTopic()
{ {
return Execute_Success_MQTT_V5(MqttQualityOfServiceLevel.AtLeastOnce); return Execute_Success_MQTT_V5(MqttQualityOfServiceLevel.AtLeastOnce);
} }
[TestMethod]
public Task Execute_Success_With_QoS_2()
{
return Execute_Success(MqttQualityOfServiceLevel.ExactlyOnce, MqttProtocolVersion.V311);
}
[TestMethod]
public Task Execute_Success_With_QoS_2_MQTT_V5()
{
return Execute_Success(MqttQualityOfServiceLevel.ExactlyOnce, MqttProtocolVersion.V500);
}
[TestMethod] [TestMethod]
public Task Execute_Success_With_QoS_2_MQTT_V5_Use_ResponseTopic() public Task Execute_Success_With_QoS_2_MQTT_V5_Use_ResponseTopic()
{ {
@@ -90,6 +115,31 @@ namespace MQTTnet.Tests
} }
} }
[TestMethod]
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
public async Task Execute_Timeout_MQTT_V5_Mixed_Clients()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
await testEnvironment.StartServer();
var responseSender = await testEnvironment.ConnectClient();
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping");
responseSender.ApplicationMessageReceivedAsync += async e =>
{
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
await CompletedTask.Instance;
};
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
using (var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build()))
{
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
}
}
}
[TestMethod] [TestMethod]
[ExpectedException(typeof(MqttCommunicationTimedOutException))] [ExpectedException(typeof(MqttCommunicationTimedOutException))]
public async Task Execute_With_Custom_Topic_Names() public async Task Execute_With_Custom_Topic_Names()
@@ -98,14 +148,24 @@ namespace MQTTnet.Tests
{ {
await testEnvironment.StartServer(); await testEnvironment.StartServer();
var requestSender = await testEnvironment.ConnectClient();
var rpcClient = await testEnvironment.ConnectRpcClient(new MqttRpcClientOptionsBuilder().WithTopicGenerationStrategy(new TestTopicStrategy()).Build()); var rpcClient = await testEnvironment.ConnectRpcClient(new MqttRpcClientOptionsBuilder().WithTopicGenerationStrategy(new TestTopicStrategy()).Build());
await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce); await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
} }
} }
[TestMethod]
public void Use_Factory()
{
var factory = new MqttFactory();
using (var client = factory.CreateMqttClient())
{
var rpcClient = factory.CreateMqttRpcClient(client);
Assert.IsNotNull(rpcClient);
}
}
async Task Execute_Success(MqttQualityOfServiceLevel qosLevel, MqttProtocolVersion protocolVersion) async Task Execute_Success(MqttQualityOfServiceLevel qosLevel, MqttProtocolVersion protocolVersion)
{ {
using (var testEnvironment = CreateTestEnvironment()) using (var testEnvironment = CreateTestEnvironment())
@@ -151,57 +211,6 @@ namespace MQTTnet.Tests
} }
} }
[TestMethod]
public async Task Execute_Success_MQTT_V5_Mixed_Clients()
{
using (var testEnvironment = CreateTestEnvironment())
{
await testEnvironment.StartServer();
var responseSender = await testEnvironment.ConnectClient();
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping", MqttQualityOfServiceLevel.AtMostOnce);
responseSender.ApplicationMessageReceivedAsync += async e =>
{
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
await responseSender.PublishStringAsync(e.ApplicationMessage.Topic + "/response", "pong");
};
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
using (var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build()))
{
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
}
}
}
[TestMethod]
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
public async Task Execute_Timeout_MQTT_V5_Mixed_Clients()
{
using (var testEnvironment = new TestEnvironment(TestContext))
{
await testEnvironment.StartServer();
var responseSender = await testEnvironment.ConnectClient();
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping", MqttQualityOfServiceLevel.AtMostOnce);
responseSender.ApplicationMessageReceivedAsync += async e =>
{
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
await CompletedTask.Instance;
};
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
using (var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build()))
{
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
}
}
}
class TestTopicStrategy : IMqttRpcClientTopicGenerationStrategy class TestTopicStrategy : IMqttRpcClientTopicGenerationStrategy
{ {
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context) public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
@@ -214,4 +223,4 @@ namespace MQTTnet.Tests
} }
} }
} }
} }
@@ -0,0 +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.
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Client;
using MQTTnet.Exceptions;
using MQTTnet.Extensions.WebSocket4Net;
using MQTTnet.Tests.Helpers;
namespace MQTTnet.Tests.Extensions
{
[TestClass]
public sealed class WebSocket4Net_Tests
{
[TestMethod]
[ExpectedException(typeof(MqttCommunicationException))]
public async Task Connect_Failed_With_Invalid_Server()
{
var factory = new MqttFactory().UseWebSocket4Net();
using (var client = factory.CreateMqttClient())
{
var options = new MqttClientOptionsBuilder().WithWebSocketServer("ws://a.b/mqtt").WithTimeout(TimeSpan.FromSeconds(2)).Build();
await client.ConnectAsync(options).ConfigureAwait(false);
}
}
[TestMethod]
public void Use_Correct_Adapter()
{
// Should not throw exception
var factory = new MqttFactory().UseWebSocket4Net();
using (var client = factory.CreateMqttClient())
{
var adapterFactory = client.GetFieldValue("_adapterFactory");
Assert.IsInstanceOfType(adapterFactory, typeof(WebSocket4NetMqttClientAdapterFactory));
}
}
[TestMethod]
public void Use_WebSocket4Net()
{
// Should not throw exception
new MqttFactory().UseWebSocket4Net();
}
}
}
@@ -15,7 +15,7 @@ using MQTTnet.Exceptions;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
using MQTTnet.Tests.Extensions; using MQTTnet.Tests.Helpers;
using MQTTnet.Tests.Mockups; using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests.Formatter namespace MQTTnet.Tests.Formatter
@@ -5,7 +5,7 @@
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Tests.Extensions namespace MQTTnet.Tests.Helpers
{ {
public static class MqttPacketWriterExtensions public static class MqttPacketWriterExtensions
{ {
@@ -0,0 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Reflection;
namespace MQTTnet.Tests.Helpers
{
public static class ReflectionExtensions
{
public static object GetFieldValue(this object source, string fieldName)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var field = source.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
{
throw new ArgumentException($"Field {fieldName} not found.");
}
return field.GetValue(source);
}
}
}
@@ -22,6 +22,7 @@
<ProjectReference Include="..\..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj" /> <ProjectReference Include="..\..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj" />
<ProjectReference Include="..\..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj" /> <ProjectReference Include="..\..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj" />
<ProjectReference Include="..\..\Source\MQTTnet\MQTTnet.csproj" /> <ProjectReference Include="..\..\Source\MQTTnet\MQTTnet.csproj" />
<ProjectReference Include="..\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+1
View File
@@ -27,6 +27,7 @@ namespace MQTTnet.Tests.Server
await dummyClient.SubscribeAsync("#", MqttQualityOfServiceLevel.AtLeastOnce); await dummyClient.SubscribeAsync("#", MqttQualityOfServiceLevel.AtLeastOnce);
dummyClient.Dispose(); dummyClient.Dispose();
await LongTestDelay();
await LongTestDelay(); await LongTestDelay();
// Now inject messages which are appended to the queue of the client. // Now inject messages which are appended to the queue of the client.
+3 -7
View File
@@ -121,10 +121,9 @@ namespace MQTTnet.Tests.Server
Assert.AreEqual(true, session.Items["can_subscribe_x"]); Assert.AreEqual(true, session.Items["can_subscribe_x"]);
} }
} }
#if !NET452
[TestMethod] [TestMethod]
[DataRow(MqttProtocolVersion.V310)]
[DataRow(MqttProtocolVersion.V311)] [DataRow(MqttProtocolVersion.V311)]
[DataRow(MqttProtocolVersion.V500)] [DataRow(MqttProtocolVersion.V500)]
public async Task Handle_Parallel_Connection_Attempts(MqttProtocolVersion protocolVersion) public async Task Handle_Parallel_Connection_Attempts(MqttProtocolVersion protocolVersion)
@@ -150,8 +149,6 @@ namespace MQTTnet.Tests.Server
// Try to connect 50 clients at the same time. // Try to connect 50 clients at the same time.
var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(i => ConnectAndSubscribe(testEnvironment, options, OnReceive))); var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(i => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
await Task.Delay(TimeSpan.FromSeconds(10));
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList(); var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
await LongTestDelay(); await LongTestDelay();
@@ -162,13 +159,12 @@ namespace MQTTnet.Tests.Server
var sendClient = await testEnvironment.ConnectClient(option2); var sendClient = await testEnvironment.ConnectClient(option2);
await sendClient.PublishStringAsync("aaa", "1"); await sendClient.PublishStringAsync("aaa", "1");
await Task.Delay(TimeSpan.FromSeconds(5)); await LongTestDelay();
Assert.AreEqual(true, hasReceive); Assert.AreEqual(true, hasReceive);
} }
} }
#endif
[DataTestMethod] [DataTestMethod]
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)] [DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)] [DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
+11 -13
View File
@@ -19,7 +19,6 @@ namespace MQTTnet.Client
public sealed class MqttClient : Disposable, IMqttClient public sealed class MqttClient : Disposable, IMqttClient
{ {
readonly IMqttClientAdapterFactory _adapterFactory; readonly IMqttClientAdapterFactory _adapterFactory;
readonly MqttApplicationMessageFactory _applicationMessageFactory = new MqttApplicationMessageFactory();
readonly AsyncEvent<MqttApplicationMessageReceivedEventArgs> _applicationMessageReceivedEvent = new AsyncEvent<MqttApplicationMessageReceivedEventArgs>(); readonly AsyncEvent<MqttApplicationMessageReceivedEventArgs> _applicationMessageReceivedEvent = new AsyncEvent<MqttApplicationMessageReceivedEventArgs>();
readonly MqttClientPublishResultFactory _clientPublishResultFactory = new MqttClientPublishResultFactory(); readonly MqttClientPublishResultFactory _clientPublishResultFactory = new MqttClientPublishResultFactory();
@@ -32,7 +31,6 @@ namespace MQTTnet.Client
readonly object _disconnectLock = new object(); readonly object _disconnectLock = new object();
readonly AsyncEvent<InspectMqttPacketEventArgs> _inspectPacketEvent = new AsyncEvent<InspectMqttPacketEventArgs>(); readonly AsyncEvent<InspectMqttPacketEventArgs> _inspectPacketEvent = new AsyncEvent<InspectMqttPacketEventArgs>();
readonly MqttNetSourceLogger _logger; readonly MqttNetSourceLogger _logger;
readonly MqttPacketFactories _packetFactories = new MqttPacketFactories();
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider(); readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
readonly IMqttNetLogger _rootLogger; readonly IMqttNetLogger _rootLogger;
@@ -207,7 +205,7 @@ namespace MQTTnet.Client
if (clientWasConnected) if (clientWasConnected)
{ {
var disconnectPacket = _packetFactories.Disconnect.Create(options); var disconnectPacket = MqttPacketFactories.Disconnect.Create(options);
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false); await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
} }
} }
@@ -241,7 +239,7 @@ namespace MQTTnet.Client
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotConnected(); ThrowIfNotConnected();
var publishPacket = _packetFactories.Publish.Create(applicationMessage); var publishPacket = MqttPacketFactories.Publish.Create(applicationMessage);
switch (applicationMessage.QualityOfServiceLevel) switch (applicationMessage.QualityOfServiceLevel)
{ {
@@ -301,7 +299,7 @@ namespace MQTTnet.Client
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotConnected(); ThrowIfNotConnected();
var subscribePacket = _packetFactories.Subscribe.Create(options); var subscribePacket = MqttPacketFactories.Subscribe.Create(options);
subscribePacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier(); subscribePacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
var subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, cancellationToken).ConfigureAwait(false); var subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, cancellationToken).ConfigureAwait(false);
@@ -319,7 +317,7 @@ namespace MQTTnet.Client
ThrowIfDisposed(); ThrowIfDisposed();
ThrowIfNotConnected(); ThrowIfNotConnected();
var unsubscribePacket = _packetFactories.Unsubscribe.Create(options); var unsubscribePacket = MqttPacketFactories.Unsubscribe.Create(options);
unsubscribePacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier(); unsubscribePacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
var unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, cancellationToken).ConfigureAwait(false); var unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, cancellationToken).ConfigureAwait(false);
@@ -347,7 +345,7 @@ namespace MQTTnet.Client
{ {
if (!eventArgs.ProcessingFailed) if (!eventArgs.ProcessingFailed)
{ {
var pubAckPacket = _packetFactories.PubAck.Create(eventArgs); var pubAckPacket = MqttPacketFactories.PubAck.Create(eventArgs);
return SendAsync(pubAckPacket, cancellationToken); return SendAsync(pubAckPacket, cancellationToken);
} }
} }
@@ -355,7 +353,7 @@ namespace MQTTnet.Client
{ {
if (!eventArgs.ProcessingFailed) if (!eventArgs.ProcessingFailed)
{ {
var pubRecPacket = _packetFactories.PubRec.Create(eventArgs); var pubRecPacket = MqttPacketFactories.PubRec.Create(eventArgs);
return SendAsync(pubRecPacket, cancellationToken); return SendAsync(pubRecPacket, cancellationToken);
} }
} }
@@ -373,7 +371,7 @@ namespace MQTTnet.Client
try try
{ {
var connectPacket = _packetFactories.Connect.Create(options); var connectPacket = MqttPacketFactories.Connect.Create(options);
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false); var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
@@ -535,7 +533,7 @@ namespace MQTTnet.Client
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket) async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
{ {
var applicationMessage = _applicationMessageFactory.Create(publishPacket); var applicationMessage = MqttApplicationMessageFactory.Create(publishPacket);
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage, publishPacket, AcknowledgeReceivedPublishPacket); var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage, publishPacket, AcknowledgeReceivedPublishPacket);
await _applicationMessageReceivedEvent.InvokeAsync(eventArgs).ConfigureAwait(false); await _applicationMessageReceivedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
@@ -601,7 +599,7 @@ namespace MQTTnet.Client
{ {
// The packet is unknown. Probably due to a restart of the 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. // So wen send this to the server to trigger a full resend of the message.
var pubRelPacket = _packetFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound); var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound);
return SendAsync(pubRelPacket, cancellationToken); return SendAsync(pubRelPacket, cancellationToken);
} }
@@ -610,7 +608,7 @@ namespace MQTTnet.Client
Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken) Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
{ {
var pubCompPacket = _packetFactories.PubComp.Create(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success); var pubCompPacket = MqttPacketFactories.PubComp.Create(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
return SendAsync(pubCompPacket, cancellationToken); return SendAsync(pubCompPacket, cancellationToken);
} }
@@ -637,7 +635,7 @@ namespace MQTTnet.Client
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false); var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
var pubRelPacket = _packetFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success); var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false); var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
@@ -6,17 +6,100 @@ using System;
namespace MQTTnet.Diagnostics namespace MQTTnet.Diagnostics
{ {
/*
* The logger uses generic parameters in order to avoid boxing of parameter values like integers etc.
*/
public static class MqttNetSourceLoggerExtensions public static class MqttNetSourceLoggerExtensions
{ {
/* public static void Error<TParameter1>(this MqttNetSourceLogger logger, Exception exception, string message, TParameter1 parameter1)
* The logger uses generic parameters in order to avoid boxing of parameter values like integers etc.
*/
public static MqttNetSourceLogger WithSource(this IMqttNetLogger logger, string source)
{ {
if (logger == null) throw new ArgumentNullException(nameof(logger)); if (!logger.IsEnabled)
{
return;
}
return new MqttNetSourceLogger(logger, source); logger.Publish(MqttNetLogLevel.Error, message, new object[] { parameter1 }, exception);
}
public static void Error<TParameter1, TParameter2>(this MqttNetSourceLogger logger, Exception exception, string message, TParameter1 parameter1, TParameter2 parameter2)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Error, message, new object[] { parameter1, parameter2 }, exception);
}
public static void Error(this MqttNetSourceLogger logger, Exception exception, string message)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Error, message, null, exception);
}
public static void Error(this MqttNetSourceLogger logger, string message)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Error, message, null, null);
}
public static void Info<TParameter1>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Info, message, new object[] { parameter1 }, null);
}
public static void Info<TParameter1, TParameter2>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1, TParameter2 parameter2)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Info, message, new object[] { parameter1, parameter2 }, null);
}
public static void Info(this MqttNetSourceLogger logger, string message)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Info, message, null, null);
}
public static void Publish<TParameter1>(this MqttNetSourceLogger logger, MqttNetLogLevel logLevel, Exception exception, string message, TParameter1 parameter1)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(logLevel, message, new object[] { parameter1 }, exception);
}
public static void Publish<TParameter1, TParameter2>(this MqttNetSourceLogger logger, MqttNetLogLevel logLevel, Exception exception, string message, TParameter1 parameter1, TParameter2 parameter2)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(logLevel, message, new object[] { parameter1, parameter2 }, exception);
} }
public static void Verbose<TParameter1>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1) public static void Verbose<TParameter1>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1)
@@ -26,7 +109,7 @@ namespace MQTTnet.Diagnostics
return; return;
} }
logger.Publish(MqttNetLogLevel.Verbose, message, new object[] {parameter1}, null); logger.Publish(MqttNetLogLevel.Verbose, message, new object[] { parameter1 }, null);
} }
public static void Verbose<TParameter1, TParameter2>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1, TParameter2 parameter2) public static void Verbose<TParameter1, TParameter2>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1, TParameter2 parameter2)
@@ -36,10 +119,14 @@ namespace MQTTnet.Diagnostics
return; return;
} }
logger.Publish(MqttNetLogLevel.Verbose, message, new object[] {parameter1, parameter2}, null); logger.Publish(MqttNetLogLevel.Verbose, message, new object[] { parameter1, parameter2 }, null);
} }
public static void Verbose<TParameter1, TParameter2, TParameter3>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1, TParameter2 parameter2, public static void Verbose<TParameter1, TParameter2, TParameter3>(
this MqttNetSourceLogger logger,
string message,
TParameter1 parameter1,
TParameter2 parameter2,
TParameter3 parameter3) TParameter3 parameter3)
{ {
if (!logger.IsEnabled) if (!logger.IsEnabled)
@@ -47,7 +134,7 @@ namespace MQTTnet.Diagnostics
return; return;
} }
logger.Publish(MqttNetLogLevel.Verbose, message, new object[] {parameter1, parameter2, parameter3}, null); logger.Publish(MqttNetLogLevel.Verbose, message, new object[] { parameter1, parameter2, parameter3 }, null);
} }
public static void Verbose(this MqttNetSourceLogger logger, string message) public static void Verbose(this MqttNetSourceLogger logger, string message)
@@ -60,134 +147,74 @@ namespace MQTTnet.Diagnostics
logger.Publish(MqttNetLogLevel.Verbose, message, null, null); logger.Publish(MqttNetLogLevel.Verbose, message, null, null);
} }
public static void Info<TParameter1>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Info, message, new object[] {parameter1}, null);
}
public static void Info<TParameter1, TParameter2>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1, TParameter2 parameter2)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Info, message, new object[] {parameter1, parameter2}, null);
}
public static void Info(this MqttNetSourceLogger logger, string message)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Info, message, null, null);
}
public static void Warning<TParameter1>(this MqttNetSourceLogger logger, Exception exception, string message, TParameter1 parameter1) public static void Warning<TParameter1>(this MqttNetSourceLogger logger, Exception exception, string message, TParameter1 parameter1)
{ {
if (!logger.IsEnabled) if (!logger.IsEnabled)
{ {
return; return;
} }
logger.Publish(MqttNetLogLevel.Warning, message, new object[] {parameter1}, exception); logger.Publish(MqttNetLogLevel.Warning, message, new object[] { parameter1 }, exception);
} }
public static void Warning<TParameter1, TParameter2>(this MqttNetSourceLogger logger, Exception exception, string message, TParameter1 parameter1, TParameter2 parameter2) public static void Warning<TParameter1, TParameter2>(this MqttNetSourceLogger logger, Exception exception, string message, TParameter1 parameter1, TParameter2 parameter2)
{ {
if (!logger.IsEnabled) if (!logger.IsEnabled)
{ {
return; return;
} }
logger.Publish(MqttNetLogLevel.Warning, message, new object[] {parameter1, parameter2}, exception); logger.Publish(MqttNetLogLevel.Warning, message, new object[] { parameter1, parameter2 }, exception);
} }
public static void Warning(this MqttNetSourceLogger logger, Exception exception, string message) public static void Warning(this MqttNetSourceLogger logger, Exception exception, string message)
{ {
if (!logger.IsEnabled) if (!logger.IsEnabled)
{ {
return; return;
} }
logger.Publish(MqttNetLogLevel.Warning, message, null, exception); logger.Publish(MqttNetLogLevel.Warning, message, null, exception);
} }
public static void Warning<TParameter1>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1) public static void Warning<TParameter1>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1)
{ {
if (!logger.IsEnabled) if (!logger.IsEnabled)
{ {
return; return;
} }
logger.Publish(MqttNetLogLevel.Warning, message, new object[] {parameter1}, null); logger.Publish(MqttNetLogLevel.Warning, message, new object[] { parameter1 }, null);
} }
public static void Warning<TParameter1, TParameter2>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1, TParameter2 parameter2) public static void Warning<TParameter1, TParameter2>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1, TParameter2 parameter2)
{ {
if (!logger.IsEnabled) if (!logger.IsEnabled)
{ {
return; return;
} }
logger.Publish(MqttNetLogLevel.Warning, message, new object[] {parameter1, parameter2}, null); logger.Publish(MqttNetLogLevel.Warning, message, new object[] { parameter1, parameter2 }, null);
} }
public static void Warning(this MqttNetSourceLogger logger, string message) public static void Warning(this MqttNetSourceLogger logger, string message)
{ {
if (!logger.IsEnabled) if (!logger.IsEnabled)
{ {
return; return;
} }
logger.Publish(MqttNetLogLevel.Warning, message, null, null); logger.Publish(MqttNetLogLevel.Warning, message, null, null);
} }
public static void Error<TParameter1>(this MqttNetSourceLogger logger, Exception exception, string message, TParameter1 parameter1) public static MqttNetSourceLogger WithSource(this IMqttNetLogger logger, string source)
{ {
if (!logger.IsEnabled) if (logger == null)
{ {
return; throw new ArgumentNullException(nameof(logger));
} }
logger.Publish(MqttNetLogLevel.Error, message, new object[] {parameter1}, exception);
}
public static void Error<TParameter1, TParameter2>(this MqttNetSourceLogger logger, Exception exception, string message, TParameter1 parameter1, TParameter2 parameter2)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Error, message, new object[] {parameter1, parameter2}, exception);
}
public static void Error(this MqttNetSourceLogger logger, Exception exception, string message) return new MqttNetSourceLogger(logger, source);
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Error, message, null, exception);
}
public static void Error(this MqttNetSourceLogger logger, string message)
{
if (!logger.IsEnabled)
{
return;
}
logger.Publish(MqttNetLogLevel.Error, message, null, null);
} }
} }
} }
@@ -7,9 +7,9 @@ using MQTTnet.Packets;
namespace MQTTnet.Formatter namespace MQTTnet.Formatter
{ {
public sealed class MqttApplicationMessageFactory public static class MqttApplicationMessageFactory
{ {
public MqttApplicationMessage Create(MqttPublishPacket publishPacket) public static MqttApplicationMessage Create(MqttPublishPacket publishPacket)
{ {
if (publishPacket == null) if (publishPacket == null)
{ {
+13 -13
View File
@@ -4,30 +4,30 @@
namespace MQTTnet.Formatter namespace MQTTnet.Formatter
{ {
public sealed class MqttPacketFactories public static class MqttPacketFactories
{ {
public MqttConnAckPacketFactory ConnAck { get; } = new MqttConnAckPacketFactory(); public static MqttConnAckPacketFactory ConnAck { get; } = new MqttConnAckPacketFactory();
public MqttConnectPacketFactory Connect { get; } = new MqttConnectPacketFactory(); public static MqttConnectPacketFactory Connect { get; } = new MqttConnectPacketFactory();
public MqttDisconnectPacketFactory Disconnect { get; } = new MqttDisconnectPacketFactory(); public static MqttDisconnectPacketFactory Disconnect { get; } = new MqttDisconnectPacketFactory();
public MqttPubAckPacketFactory PubAck { get; } = new MqttPubAckPacketFactory(); public static MqttPubAckPacketFactory PubAck { get; } = new MqttPubAckPacketFactory();
public MqttPubCompPacketFactory PubComp { get; } = new MqttPubCompPacketFactory(); public static MqttPubCompPacketFactory PubComp { get; } = new MqttPubCompPacketFactory();
public MqttPublishPacketFactory Publish { get; } = new MqttPublishPacketFactory(); public static MqttPublishPacketFactory Publish { get; } = new MqttPublishPacketFactory();
public MqttPubRecPacketFactory PubRec { get; } = new MqttPubRecPacketFactory(); public static MqttPubRecPacketFactory PubRec { get; } = new MqttPubRecPacketFactory();
public MqttPubRelPacketFactory PubRel { get; } = new MqttPubRelPacketFactory(); public static MqttPubRelPacketFactory PubRel { get; } = new MqttPubRelPacketFactory();
public MqttSubAckPacketFactory SubAck { get; } = new MqttSubAckPacketFactory(); public static MqttSubAckPacketFactory SubAck { get; } = new MqttSubAckPacketFactory();
public MqttSubscribePacketFactory Subscribe { get; } = new MqttSubscribePacketFactory(); public static MqttSubscribePacketFactory Subscribe { get; } = new MqttSubscribePacketFactory();
public MqttUnsubAckPacketFactory UnsubAck { get; } = new MqttUnsubAckPacketFactory(); public static MqttUnsubAckPacketFactory UnsubAck { get; } = new MqttUnsubAckPacketFactory();
public MqttUnsubscribePacketFactory Unsubscribe { get; } = new MqttUnsubscribePacketFactory(); public static MqttUnsubscribePacketFactory Unsubscribe { get; } = new MqttUnsubscribePacketFactory();
} }
} }
@@ -78,7 +78,7 @@ namespace MQTTnet.Formatter.V3
return DecodeSubscribePacket(receivedMqttPacket.Body); return DecodeSubscribePacket(receivedMqttPacket.Body);
case MqttControlPacketType.SubAck: case MqttControlPacketType.SubAck:
return DecodeSubAckPacket(receivedMqttPacket.Body); return DecodeSubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Unsubscibe: case MqttControlPacketType.Unsubscribe:
return DecodeUnsubscribePacket(receivedMqttPacket.Body); return DecodeUnsubscribePacket(receivedMqttPacket.Body);
case MqttControlPacketType.UnsubAck: case MqttControlPacketType.UnsubAck:
return DecodeUnsubAckPacket(receivedMqttPacket.Body); return DecodeUnsubAckPacket(receivedMqttPacket.Body);
@@ -783,7 +783,7 @@ namespace MQTTnet.Formatter.V3
} }
} }
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02); return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Unsubscribe, 0x02);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -54,7 +54,7 @@ namespace MQTTnet.Formatter.V5
return DecodeSubscribePacket(receivedMqttPacket.Body); return DecodeSubscribePacket(receivedMqttPacket.Body);
case MqttControlPacketType.SubAck: case MqttControlPacketType.SubAck:
return DecodeSubAckPacket(receivedMqttPacket.Body); return DecodeSubAckPacket(receivedMqttPacket.Body);
case MqttControlPacketType.Unsubscibe: case MqttControlPacketType.Unsubscribe:
return DecodeUnsubscribePacket(receivedMqttPacket.Body); return DecodeUnsubscribePacket(receivedMqttPacket.Body);
case MqttControlPacketType.UnsubAck: case MqttControlPacketType.UnsubAck:
return DecodeUnsubAckPacket(receivedMqttPacket.Body); return DecodeUnsubAckPacket(receivedMqttPacket.Body);
@@ -524,7 +524,7 @@ namespace MQTTnet.Formatter.V5
_bufferWriter.WriteString(topicFilter); _bufferWriter.WriteString(topicFilter);
} }
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02); return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Unsubscribe, 0x02);
} }
static void ThrowIfPacketIdentifierIsInvalid(ushort packetIdentifier, MqttPacket packet) static void ThrowIfPacketIdentifierIsInvalid(ushort packetIdentifier, MqttPacket packet)
@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading;
namespace MQTTnet.Internal
{
public static class CancellationTokenSourceExtensions
{
public static bool TryCancel(this CancellationTokenSource cancellationTokenSource, bool throwOnFirstException = false)
{
if (cancellationTokenSource == null)
{
return false;
}
try
{
// Checking the _IsCancellationRequested_ here will not throw an
// "ObjectDisposedException" as the getter of the property "Token" will do!
if (cancellationTokenSource.IsCancellationRequested)
{
return false;
}
cancellationTokenSource.Cancel(throwOnFirstException);
return true;
}
catch (ObjectDisposedException)
{
return false;
}
}
}
}
@@ -0,0 +1,102 @@
// 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;
namespace MQTTnet.Packets
{
public static class MqttPacketExtensions
{
public static string GetRfcName(this MqttPacket packet)
{
if (packet == null)
{
throw new ArgumentNullException(nameof(packet));
}
switch (packet)
{
case MqttConnectPacket _:
{
return "CONNECT";
}
case MqttConnAckPacket _:
{
return "CONNACK";
}
case MqttAuthPacket _:
{
return "AUTH";
}
case MqttDisconnectPacket _:
{
return "DISCONNECT";
}
case MqttPingReqPacket _:
{
return "PINGREQ";
}
case MqttPingRespPacket _:
{
return "PINGRESP";
}
case MqttSubscribePacket _:
{
return "SUBSCRIBE";
}
case MqttSubAckPacket _:
{
return "SUBACK";
}
case MqttUnsubscribePacket _:
{
return "UNSUBSCRIBE";
}
case MqttUnsubAckPacket _:
{
return "UNSUBACK";
}
case MqttPublishPacket _:
{
return "PUBLISH";
}
case MqttPubAckPacket _:
{
return "PUBACK";
}
case MqttPubRelPacket _:
{
return "PUBREL";
}
case MqttPubRecPacket _:
{
return "PUBREC";
}
case MqttPubCompPacket _:
{
return "PUBCOMP";
}
default:
{
return packet.GetType().Name;
}
}
}
}
}
@@ -15,7 +15,7 @@ namespace MQTTnet.Protocol
PubComp = 7, PubComp = 7,
Subscribe = 8, Subscribe = 8,
SubAck = 9, SubAck = 9,
Unsubscibe = 10, Unsubscribe = 10,
UnsubAck = 11, UnsubAck = 11,
PingReq = 12, PingReq = 12,
PingResp = 13, PingResp = 13,
+86 -82
View File
@@ -12,27 +12,21 @@ using MQTTnet.Diagnostics;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Internal; using MQTTnet.Internal;
using MQTTnet.PacketDispatcher;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Server namespace MQTTnet.Server
{ {
public sealed class MqttClient public sealed class MqttClient : IDisposable
{ {
readonly MqttApplicationMessageFactory _applicationMessageFactory = new MqttApplicationMessageFactory();
readonly MqttConnectPacket _connectPacket; readonly MqttConnectPacket _connectPacket;
readonly MqttServerEventContainer _eventContainer; readonly MqttServerEventContainer _eventContainer;
readonly MqttNetSourceLogger _logger; readonly MqttNetSourceLogger _logger;
readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
readonly MqttPacketFactories _packetFactories = new MqttPacketFactories();
readonly MqttServerOptions _serverOptions; readonly MqttServerOptions _serverOptions;
readonly MqttClientSessionsManager _sessionsManager; readonly MqttClientSessionsManager _sessionsManager;
readonly Dictionary<ushort, string> _topicAlias = new Dictionary<ushort, string>(); readonly Dictionary<ushort, string> _topicAlias = new Dictionary<ushort, string>();
CancellationTokenSource _cancellationToken; CancellationTokenSource _cancellationToken = new CancellationTokenSource();
bool _disconnectPacketSent; bool _disconnectPacketSent;
public MqttClient( public MqttClient(
@@ -45,14 +39,13 @@ namespace MQTTnet.Server
IMqttNetLogger logger) IMqttNetLogger logger)
{ {
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions)); _serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
_eventContainer = eventContainer; _eventContainer = eventContainer ?? throw new ArgumentNullException(nameof(eventContainer));
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager)); _sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
ChannelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter)); ChannelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter));
Endpoint = channelAdapter.Endpoint; Endpoint = channelAdapter.Endpoint;
Session = session ?? throw new ArgumentNullException(nameof(session)); Session = session ?? throw new ArgumentNullException(nameof(session));
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
if (logger == null) if (logger == null)
{ {
@@ -80,6 +73,11 @@ namespace MQTTnet.Server
public MqttClientStatistics Statistics { get; } = new MqttClientStatistics(); public MqttClientStatistics Statistics { get; } = new MqttClientStatistics();
public void Dispose()
{
_cancellationToken?.Dispose();
}
public void ResetStatistics() public void ResetStatistics()
{ {
ChannelAdapter.ResetStatistics(); ChannelAdapter.ResetStatistics();
@@ -88,46 +86,43 @@ namespace MQTTnet.Server
public async Task RunAsync() public async Task RunAsync()
{ {
_logger.Info("Client '{0}': Session started.", Id); _logger.Info("Client '{0}': Session started", Id);
Session.LatestConnectPacket = _connectPacket; Session.LatestConnectPacket = _connectPacket;
Session.WillMessageSent = false; Session.WillMessageSent = false;
using (_cancellationToken = new CancellationTokenSource()) try
{ {
var cancellationToken = _cancellationToken.Token; var cancellationToken = _cancellationToken.Token;
try _ = Task.Factory.StartNew(() => SendPacketsLoop(cancellationToken), cancellationToken, TaskCreationOptions.PreferFairness, TaskScheduler.Default)
{ .ConfigureAwait(false);
_ = Task.Factory.StartNew(() => SendPacketsLoop(cancellationToken), cancellationToken, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false);
IsRunning = true; IsRunning = true;
await ReceivePackagesLoop(cancellationToken).ConfigureAwait(false); await ReceivePackagesLoop(cancellationToken).ConfigureAwait(false);
}
finally
{
IsRunning = false;
_cancellationToken?.Cancel();
_cancellationToken = null;
}
} }
finally
{
IsRunning = false;
_packetDispatcher.CancelAll(); _cancellationToken?.TryCancel();
_cancellationToken?.Dispose();
_cancellationToken = null;
}
if (!IsTakenOver && !IsCleanDisconnect && Session.LatestConnectPacket.WillFlag && !Session.WillMessageSent) if (!IsTakenOver && !IsCleanDisconnect && Session.LatestConnectPacket.WillFlag && !Session.WillMessageSent)
{ {
var willPublishPacket = _packetFactories.Publish.Create(Session.LatestConnectPacket); var willPublishPacket = MqttPacketFactories.Publish.Create(Session.LatestConnectPacket);
var willApplicationMessage = _applicationMessageFactory.Create(willPublishPacket); var willApplicationMessage = MqttApplicationMessageFactory.Create(willPublishPacket);
_ = _sessionsManager.DispatchApplicationMessage(Id, willApplicationMessage); _ = _sessionsManager.DispatchApplicationMessage(Id, willApplicationMessage);
Session.WillMessageSent = true; Session.WillMessageSent = true;
_logger.Info("Client '{0}': Published will message.", Id); _logger.Info("Client '{0}': Published will message", Id);
} }
_logger.Info("Client '{0}': Connection stopped.", Id); _logger.Info("Client '{0}': Connection stopped", Id);
} }
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken) public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
@@ -181,6 +176,12 @@ namespace MQTTnet.Server
return CompletedTask.Instance; return CompletedTask.Instance;
} }
void HandleIncomingPingReqPacket()
{
// See: The Server MUST send a PINGRESP packet in response to a PINGREQ packet [MQTT-3.12.4-1].
Session.EnqueueHealthPacket(new MqttPacketBusItem(MqttPingRespPacket.Instance));
}
Task HandleIncomingPubAckPacket(MqttPubAckPacket pubAckPacket) Task HandleIncomingPubAckPacket(MqttPubAckPacket pubAckPacket)
{ {
var acknowledgedPublishPacket = Session.AcknowledgePublishPacket(pubAckPacket.PacketIdentifier); var acknowledgedPublishPacket = Session.AcknowledgePublishPacket(pubAckPacket.PacketIdentifier);
@@ -210,7 +211,7 @@ namespace MQTTnet.Server
HandleTopicAlias(publishPacket); HandleTopicAlias(publishPacket);
InterceptingPublishEventArgs interceptingPublishEventArgs = null; InterceptingPublishEventArgs interceptingPublishEventArgs = null;
var applicationMessage = _applicationMessageFactory.Create(publishPacket); var applicationMessage = MqttApplicationMessageFactory.Create(publishPacket);
var closeConnection = false; var closeConnection = false;
var processPublish = true; var processPublish = true;
@@ -253,19 +254,19 @@ namespace MQTTnet.Server
} }
case MqttQualityOfServiceLevel.AtLeastOnce: case MqttQualityOfServiceLevel.AtLeastOnce:
{ {
var pubAckPacket = _packetFactories.PubAck.Create(publishPacket, interceptingPublishEventArgs, dispatchResult); var pubAckPacket = MqttPacketFactories.PubAck.Create(publishPacket, interceptingPublishEventArgs, dispatchResult);
Session.EnqueueControlPacket(new MqttPacketBusItem(pubAckPacket)); Session.EnqueueControlPacket(new MqttPacketBusItem(pubAckPacket));
break; break;
} }
case MqttQualityOfServiceLevel.ExactlyOnce: case MqttQualityOfServiceLevel.ExactlyOnce:
{ {
var pubRecPacket = _packetFactories.PubRec.Create(publishPacket, interceptingPublishEventArgs, dispatchResult); var pubRecPacket = MqttPacketFactories.PubRec.Create(publishPacket, interceptingPublishEventArgs, dispatchResult);
Session.EnqueueControlPacket(new MqttPacketBusItem(pubRecPacket)); Session.EnqueueControlPacket(new MqttPacketBusItem(pubRecPacket));
break; break;
} }
default: default:
{ {
throw new MqttCommunicationException("Received a not supported QoS level."); throw new MqttCommunicationException("Received a not supported QoS level");
} }
} }
} }
@@ -274,7 +275,7 @@ namespace MQTTnet.Server
{ {
// Do not fire the event _ClientAcknowledgedPublishPacket_ here because the QoS 2 process is only finished // Do not fire the event _ClientAcknowledgedPublishPacket_ here because the QoS 2 process is only finished
// properly when the client has sent the PUBCOMP packet. // properly when the client has sent the PUBCOMP packet.
var pubRelPacket = _packetFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success); var pubRelPacket = MqttPacketFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
Session.EnqueueControlPacket(new MqttPacketBusItem(pubRelPacket)); Session.EnqueueControlPacket(new MqttPacketBusItem(pubRelPacket));
return CompletedTask.Instance; return CompletedTask.Instance;
@@ -282,7 +283,7 @@ namespace MQTTnet.Server
void HandleIncomingPubRelPacket(MqttPubRelPacket pubRelPacket) void HandleIncomingPubRelPacket(MqttPubRelPacket pubRelPacket)
{ {
var pubCompPacket = _packetFactories.PubComp.Create(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success); var pubCompPacket = MqttPacketFactories.PubComp.Create(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
Session.EnqueueControlPacket(new MqttPacketBusItem(pubCompPacket)); Session.EnqueueControlPacket(new MqttPacketBusItem(pubCompPacket));
} }
@@ -290,7 +291,7 @@ namespace MQTTnet.Server
{ {
var subscribeResult = await Session.Subscribe(subscribePacket, cancellationToken).ConfigureAwait(false); var subscribeResult = await Session.Subscribe(subscribePacket, cancellationToken).ConfigureAwait(false);
var subAckPacket = _packetFactories.SubAck.Create(subscribePacket, subscribeResult); var subAckPacket = MqttPacketFactories.SubAck.Create(subscribePacket, subscribeResult);
Session.EnqueueControlPacket(new MqttPacketBusItem(subAckPacket)); Session.EnqueueControlPacket(new MqttPacketBusItem(subAckPacket));
@@ -304,7 +305,7 @@ namespace MQTTnet.Server
{ {
foreach (var retainedMessageMatch in subscribeResult.RetainedMessages) foreach (var retainedMessageMatch in subscribeResult.RetainedMessages)
{ {
var publishPacket = _packetFactories.Publish.Create(retainedMessageMatch); var publishPacket = MqttPacketFactories.Publish.Create(retainedMessageMatch);
Session.EnqueueDataPacket(new MqttPacketBusItem(publishPacket)); Session.EnqueueDataPacket(new MqttPacketBusItem(publishPacket));
} }
} }
@@ -314,7 +315,7 @@ namespace MQTTnet.Server
{ {
var unsubscribeResult = await Session.Unsubscribe(unsubscribePacket, cancellationToken).ConfigureAwait(false); var unsubscribeResult = await Session.Unsubscribe(unsubscribePacket, cancellationToken).ConfigureAwait(false);
var unsubAckPacket = _packetFactories.UnsubAck.Create(unsubscribePacket, unsubscribeResult); var unsubAckPacket = MqttPacketFactories.UnsubAck.Create(unsubscribePacket, unsubscribeResult);
Session.EnqueueControlPacket(new MqttPacketBusItem(unsubAckPacket)); Session.EnqueueControlPacket(new MqttPacketBusItem(unsubAckPacket));
@@ -345,7 +346,7 @@ namespace MQTTnet.Server
} }
else else
{ {
_logger.Warning("Client '{0}': Received invalid topic alias ({1}).", Id, publishPacket.TopicAlias); _logger.Warning("Client '{0}': Received invalid topic alias ({1})", Id, publishPacket.TopicAlias);
} }
} }
} }
@@ -371,6 +372,7 @@ namespace MQTTnet.Server
async Task ReceivePackagesLoop(CancellationToken cancellationToken) async Task ReceivePackagesLoop(CancellationToken cancellationToken)
{ {
MqttPacket currentPacket = null;
try try
{ {
// We do not listen for the cancellation token here because the internal buffer might still // We do not listen for the cancellation token here because the internal buffer might still
@@ -380,12 +382,12 @@ namespace MQTTnet.Server
{ {
await Task.Yield(); await Task.Yield();
var packet = await ChannelAdapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false); currentPacket = await ChannelAdapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false);
if (packet == null) if (currentPacket == null)
{ {
return; return;
} }
// Check for cancellation again because receive packet might block some time. // Check for cancellation again because receive packet might block some time.
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
@@ -398,73 +400,69 @@ namespace MQTTnet.Server
{ {
return; return;
} }
var processPacket = true; var processPacket = true;
if (_eventContainer.InterceptingInboundPacketEvent.HasHandlers) if (_eventContainer.InterceptingInboundPacketEvent.HasHandlers)
{ {
var interceptingPacketEventArgs = new InterceptingPacketEventArgs(cancellationToken, Id, Endpoint, packet, Session.Items); var interceptingPacketEventArgs = new InterceptingPacketEventArgs(cancellationToken, Id, Endpoint, currentPacket, Session.Items);
await _eventContainer.InterceptingInboundPacketEvent.InvokeAsync(interceptingPacketEventArgs).ConfigureAwait(false); await _eventContainer.InterceptingInboundPacketEvent.InvokeAsync(interceptingPacketEventArgs).ConfigureAwait(false);
packet = interceptingPacketEventArgs.Packet; currentPacket = interceptingPacketEventArgs.Packet;
processPacket = interceptingPacketEventArgs.ProcessPacket; processPacket = interceptingPacketEventArgs.ProcessPacket;
} }
if (!processPacket || packet == null) if (!processPacket || currentPacket == null)
{ {
// Restart the receiving process to get the next packet ignoring the current one.. // Restart the receiving process to get the next packet ignoring the current one..
continue; continue;
} }
Statistics.HandleReceivedPacket(packet); Statistics.HandleReceivedPacket(currentPacket);
if (packet is MqttPublishPacket publishPacket) if (currentPacket is MqttPublishPacket publishPacket)
{ {
await HandleIncomingPublishPacket(publishPacket, cancellationToken).ConfigureAwait(false); await HandleIncomingPublishPacket(publishPacket, cancellationToken).ConfigureAwait(false);
} }
else if (packet is MqttPubAckPacket pubAckPacket) else if (currentPacket is MqttPubAckPacket pubAckPacket)
{ {
await HandleIncomingPubAckPacket(pubAckPacket).ConfigureAwait(false); await HandleIncomingPubAckPacket(pubAckPacket).ConfigureAwait(false);
} }
else if (packet is MqttPubCompPacket pubCompPacket) else if (currentPacket is MqttPubCompPacket pubCompPacket)
{ {
await HandleIncomingPubCompPacket(pubCompPacket).ConfigureAwait(false); await HandleIncomingPubCompPacket(pubCompPacket).ConfigureAwait(false);
} }
else if (packet is MqttPubRecPacket pubRecPacket) else if (currentPacket is MqttPubRecPacket pubRecPacket)
{ {
await HandleIncomingPubRecPacket(pubRecPacket).ConfigureAwait(false); await HandleIncomingPubRecPacket(pubRecPacket).ConfigureAwait(false);
} }
else if (packet is MqttPubRelPacket pubRelPacket) else if (currentPacket is MqttPubRelPacket pubRelPacket)
{ {
HandleIncomingPubRelPacket(pubRelPacket); HandleIncomingPubRelPacket(pubRelPacket);
} }
else if (packet is MqttSubscribePacket subscribePacket) else if (currentPacket is MqttSubscribePacket subscribePacket)
{ {
await HandleIncomingSubscribePacket(subscribePacket, cancellationToken).ConfigureAwait(false); await HandleIncomingSubscribePacket(subscribePacket, cancellationToken).ConfigureAwait(false);
} }
else if (packet is MqttUnsubscribePacket unsubscribePacket) else if (currentPacket is MqttUnsubscribePacket unsubscribePacket)
{ {
await HandleIncomingUnsubscribePacket(unsubscribePacket, cancellationToken).ConfigureAwait(false); await HandleIncomingUnsubscribePacket(unsubscribePacket, cancellationToken).ConfigureAwait(false);
} }
else if (packet is MqttPingReqPacket) else if (currentPacket is MqttPingReqPacket)
{ {
// See: The Server MUST send a PINGRESP packet in response to a PINGREQ packet [MQTT-3.12.4-1]. HandleIncomingPingReqPacket();
Session.EnqueueHealthPacket(new MqttPacketBusItem(MqttPingRespPacket.Instance));
} }
else if (packet is MqttPingRespPacket) else if (currentPacket is MqttPingRespPacket)
{ {
throw new MqttProtocolViolationException("A PINGRESP Packet is sent by the Server to the Client in response to a PINGREQ Packet only."); throw new MqttProtocolViolationException("A PINGRESP Packet is sent by the Server to the Client in response to a PINGREQ Packet only.");
} }
else if (packet is MqttDisconnectPacket) else if (currentPacket is MqttDisconnectPacket)
{ {
IsCleanDisconnect = true; IsCleanDisconnect = true;
return; return;
} }
else else
{ {
if (!_packetDispatcher.TryDispatch(packet)) throw new MqttProtocolViolationException("Packet not allowed");
{
throw new MqttProtocolViolationException($"Received packet '{packet}' at an unexpected time.");
}
} }
} }
} }
@@ -475,11 +473,25 @@ namespace MQTTnet.Server
{ {
if (exception is MqttCommunicationException) if (exception is MqttCommunicationException)
{ {
_logger.Warning(exception, "Client '{0}': Communication exception while receiving client packets.", Id); _logger.Warning(exception, "Client '{0}': Communication exception while receiving packets", Id);
return;
}
var logLevel = MqttNetLogLevel.Error;
if (!IsRunning)
{
// There was an exception but the connection is already closed. So there is no chance to send a response to the client etc.
logLevel = MqttNetLogLevel.Warning;
}
if (currentPacket == null)
{
_logger.Publish(logLevel, exception, "Client '{0}': Error while receiving packets", Id);
} }
else else
{ {
_logger.Error(exception, "Client '{0}': Error while receiving client packets.", Id); _logger.Publish(logLevel, exception, "Client '{0}': Error while processing {1} packet", Id, currentPacket.GetRfcName());
} }
} }
} }
@@ -531,15 +543,15 @@ namespace MQTTnet.Server
{ {
if (exception is MqttCommunicationTimedOutException) if (exception is MqttCommunicationTimedOutException)
{ {
_logger.Warning(exception, "Client '{0}': Sending publish packet failed: Timeout.", Id); _logger.Warning(exception, "Client '{0}': Sending PUBLISH packet failed due to timeout", Id);
} }
else if (exception is MqttCommunicationException) else if (exception is MqttCommunicationException)
{ {
_logger.Warning(exception, "Client '{0}': Sending publish packet failed: Communication exception.", Id); _logger.Warning(exception, "Client '{0}': Sending PUBLISH packet failed due to communication exception", Id);
} }
else else
{ {
_logger.Error(exception, "Client '{0}': Sending publish packet failed.", Id); _logger.Error(exception, "Client '{0}': Sending PUBLISH packet failed", Id);
} }
if (packetBusItem?.Packet is MqttPublishPacket publishPacket) if (packetBusItem?.Packet is MqttPublishPacket publishPacket)
@@ -557,15 +569,7 @@ namespace MQTTnet.Server
void StopInternal() void StopInternal()
{ {
try _cancellationToken?.TryCancel();
{
_cancellationToken?.Cancel();
}
catch (ObjectDisposedException)
{
// This can happen when connections are created and dropped very quickly.
// It is not an issue if the cancellation token cannot be canceled multiple times.
}
} }
async Task TrySendDisconnectPacket(MqttDisconnectReasonCode reasonCode) async Task TrySendDisconnectPacket(MqttDisconnectReasonCode reasonCode)
@@ -574,8 +578,8 @@ namespace MQTTnet.Server
{ {
// This also indicates that it was tried at least! // This also indicates that it was tried at least!
_disconnectPacketSent = true; _disconnectPacketSent = true;
var disconnectPacket = _packetFactories.Disconnect.Create(reasonCode); var disconnectPacket = MqttPacketFactories.Disconnect.Create(reasonCode);
using (var timeout = new CancellationTokenSource(_serverOptions.DefaultCommunicationTimeout)) using (var timeout = new CancellationTokenSource(_serverOptions.DefaultCommunicationTimeout))
{ {
@@ -584,7 +588,7 @@ namespace MQTTnet.Server
} }
catch (Exception exception) catch (Exception exception)
{ {
_logger.Warning(exception, "Client '{0}': Error while sending DISCONNECT packet (ReasonCode = {1}).", Id, reasonCode); _logger.Warning(exception, "Client '{0}': Error while sending DISCONNECT packet (ReasonCode = {1})", Id, reasonCode);
} }
} }
} }
@@ -28,7 +28,6 @@ namespace MQTTnet.Server
readonly MqttServerEventContainer _eventContainer; readonly MqttServerEventContainer _eventContainer;
readonly MqttNetSourceLogger _logger; readonly MqttNetSourceLogger _logger;
readonly MqttServerOptions _options; readonly MqttServerOptions _options;
readonly MqttPacketFactories _packetFactories = new MqttPacketFactories();
readonly MqttRetainedMessagesManager _retainedMessagesManager; readonly MqttRetainedMessagesManager _retainedMessagesManager;
readonly IMqttNetLogger _rootLogger; readonly IMqttNetLogger _rootLogger;
@@ -166,7 +165,7 @@ namespace MQTTnet.Server
continue; continue;
} }
var newPublishPacket = _packetFactories.Publish.Create(applicationMessage); var newPublishPacket = MqttPacketFactories.Publish.Create(applicationMessage);
newPublishPacket.QualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel; newPublishPacket.QualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel;
newPublishPacket.SubscriptionIdentifiers = checkSubscriptionsResult.SubscriptionIdentifiers; newPublishPacket.SubscriptionIdentifiers = checkSubscriptionsResult.SubscriptionIdentifiers;
@@ -288,7 +287,7 @@ namespace MQTTnet.Server
} }
var validatingConnectionEventArgs = await ValidateConnection(connectPacket, channelAdapter).ConfigureAwait(false); var validatingConnectionEventArgs = await ValidateConnection(connectPacket, channelAdapter).ConfigureAwait(false);
var connAckPacket = _packetFactories.ConnAck.Create(validatingConnectionEventArgs); var connAckPacket = MqttPacketFactories.ConnAck.Create(validatingConnectionEventArgs);
if (validatingConnectionEventArgs.ReasonCode != MqttConnectReasonCode.Success) if (validatingConnectionEventArgs.ReasonCode != MqttConnectReasonCode.Success)
{ {
@@ -430,7 +429,7 @@ namespace MQTTnet.Server
{ {
foreach (var retainedMessageMatch in subscribeResult.RetainedMessages) foreach (var retainedMessageMatch in subscribeResult.RetainedMessages)
{ {
var publishPacket = _packetFactories.Publish.Create(retainedMessageMatch); var publishPacket = MqttPacketFactories.Publish.Create(retainedMessageMatch);
clientSession.EnqueueDataPacket(new MqttPacketBusItem(publishPacket)); clientSession.EnqueueDataPacket(new MqttPacketBusItem(publishPacket));
} }
} }