mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-11-05 14:02:55 +08:00
v4.0.0 (#1357)
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// 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.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -23,13 +27,13 @@ namespace MQTTnet.Adapter
|
||||
|
||||
bool IsReadingPacket { get; }
|
||||
|
||||
Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||
Task ConnectAsync(CancellationToken cancellationToken);
|
||||
|
||||
Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||
Task DisconnectAsync(CancellationToken cancellationToken);
|
||||
|
||||
Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken);
|
||||
Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken);
|
||||
|
||||
Task<MqttBasePacket> ReceivePacketAsync(CancellationToken cancellationToken);
|
||||
Task<MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken);
|
||||
|
||||
void ResetStatistics();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
using MQTTnet.Client.Options;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public interface IMqttClientAdapterFactory
|
||||
{
|
||||
IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options);
|
||||
IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
using System;
|
||||
// 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 MQTTnet.Diagnostics;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
@@ -8,7 +13,7 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
Task StartAsync(IMqttServerOptions options);
|
||||
Task StartAsync(MqttServerOptions options, IMqttNetLogger logger);
|
||||
Task StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
// 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.IO;
|
||||
using System.Net.Sockets;
|
||||
@@ -11,8 +9,13 @@ using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Diagnostics.PacketInspection;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
@@ -20,152 +23,111 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
const uint ErrorOperationAborted = 0x800703E3;
|
||||
const int ReadBufferSize = 4096;
|
||||
|
||||
readonly IMqttChannel _channel;
|
||||
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
|
||||
readonly MqttPacketInspector _packetInspector;
|
||||
|
||||
readonly byte[] _singleByteBuffer = new byte[1];
|
||||
readonly byte[] _fixedHeaderBuffer = new byte[2];
|
||||
|
||||
readonly MqttPacketInspectorHandler _packetInspectorHandler;
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
readonly IMqttChannel _channel;
|
||||
|
||||
readonly AsyncLock _syncRoot = new AsyncLock();
|
||||
|
||||
long _bytesReceived;
|
||||
long _bytesSent;
|
||||
|
||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, IMqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||
public MqttChannelAdapter(IMqttChannel channel, MqttPacketFormatterAdapter packetFormatterAdapter, MqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||
{
|
||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
||||
_packetInspector = packetInspector;
|
||||
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
|
||||
_packetInspectorHandler = new MqttPacketInspectorHandler(packetInspector, logger);
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.WithSource(nameof(MqttChannelAdapter));
|
||||
}
|
||||
|
||||
public string Endpoint => _channel.Endpoint;
|
||||
|
||||
public bool IsSecureConnection => _channel.IsSecureConnection;
|
||||
|
||||
public X509Certificate2 ClientCertificate => _channel.ClientCertificate;
|
||||
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
public long BytesReceived => Interlocked.Read(ref _bytesReceived);
|
||||
|
||||
public long BytesSent => Interlocked.Read(ref _bytesSent);
|
||||
|
||||
public long BytesReceived => Interlocked.Read(ref _bytesReceived);
|
||||
public X509Certificate2 ClientCertificate => _channel.ClientCertificate;
|
||||
|
||||
public string Endpoint => _channel.Endpoint;
|
||||
|
||||
public bool IsReadingPacket { get; private set; }
|
||||
|
||||
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
public bool IsSecureConnection => _channel.IsSecureConnection;
|
||||
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
try
|
||||
{
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await _channel.ConnectAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(t => _channel.ConnectAsync(t), timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
await _channel.ConnectAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (IsWrappedException(exception))
|
||||
if (!WrapAndThrowException(exception))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
WrapAndThrowException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
public async Task DisconnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
try
|
||||
{
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await _channel.DisconnectAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await MqttTaskTimeout.WaitAsync(
|
||||
t => _channel.DisconnectAsync(t), timeout, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
await _channel.DisconnectAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (IsWrappedException(exception))
|
||||
if (!WrapAndThrowException(exception))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
WrapAndThrowException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
// Check for cancellation here again because "WaitAsync" might take some time.
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
var packetData = PacketFormatterAdapter.Encode(packet);
|
||||
_packetInspectorHandler.BeginSendPacket(packetData);
|
||||
|
||||
await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Interlocked.Add(ref _bytesReceived, packetData.Count);
|
||||
|
||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (IsWrappedException(exception))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
WrapAndThrowException(exception);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MqttBasePacket> ReceivePacketAsync(CancellationToken cancellationToken)
|
||||
public async Task<MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
try
|
||||
{
|
||||
_packetInspectorHandler.BeginReceivePacket();
|
||||
_packetInspector?.BeginReceivePacket();
|
||||
|
||||
var receivedPacket = await ReceiveAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (receivedPacket == null || cancellationToken.IsCancellationRequested)
|
||||
ReceivedMqttPacket receivedPacket;
|
||||
var receivedPacketTask = ReceiveAsync(cancellationToken);
|
||||
if (receivedPacketTask.IsCompleted)
|
||||
{
|
||||
receivedPacket = receivedPacketTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
receivedPacket = await receivedPacketTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (receivedPacket.TotalLength == 0 || cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_packetInspectorHandler.EndReceivePacket();
|
||||
_packetInspector?.EndReceivePacket();
|
||||
|
||||
Interlocked.Add(ref _bytesSent, receivedPacket.TotalLength);
|
||||
|
||||
@@ -192,12 +154,10 @@ namespace MQTTnet.Adapter
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (IsWrappedException(exception))
|
||||
if (!WrapAndThrowException(exception))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
WrapAndThrowException(exception);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -209,6 +169,48 @@ namespace MQTTnet.Adapter
|
||||
Interlocked.Exchange(ref _bytesSent, 0L);
|
||||
}
|
||||
|
||||
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
// This lock makes sure that multiple threads can send packets at the same time.
|
||||
// This is required when a disconnect is sent from another thread while the
|
||||
// worker thread is still sending publish packets etc.
|
||||
using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
// Check for cancellation here again because "WaitAsync" might take some time.
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
var packetBuffer = PacketFormatterAdapter.Encode(packet);
|
||||
_packetInspector?.BeginSendPacket(packetBuffer);
|
||||
|
||||
_logger.Verbose("TX ({0} bytes) >>> {1}", packetBuffer.Length, packet);
|
||||
|
||||
await _channel.WriteAsync(packetBuffer.Packet.Array, packetBuffer.Packet.Offset, packetBuffer.Packet.Count, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (packetBuffer.Payload.Count > 0)
|
||||
{
|
||||
await _channel.WriteAsync(packetBuffer.Payload.Array, packetBuffer.Payload.Offset, packetBuffer.Payload.Count, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Interlocked.Add(ref _bytesReceived, packetBuffer.Length);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (!WrapAndThrowException(exception))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PacketFormatterAdapter.Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
@@ -220,23 +222,118 @@ namespace MQTTnet.Adapter
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
async Task<int> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
|
||||
{
|
||||
var offset = 0;
|
||||
var multiplier = 128;
|
||||
var value = initialEncodedByte & 127;
|
||||
int encodedByte = initialEncodedByte;
|
||||
|
||||
while ((encodedByte & 128) != 0)
|
||||
{
|
||||
offset++;
|
||||
if (offset > 3)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Remaining length is invalid.");
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (readCount == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
_packetInspector?.FillReceiveBuffer(_singleByteBuffer);
|
||||
|
||||
encodedByte = _singleByteBuffer[0];
|
||||
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
async Task<ReadFixedHeaderResult> ReadFixedHeaderAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
|
||||
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
|
||||
var buffer = _fixedHeaderBuffer;
|
||||
var totalBytesRead = 0;
|
||||
|
||||
while (totalBytesRead < buffer.Length)
|
||||
{
|
||||
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return ReadFixedHeaderResult.Cancelled;
|
||||
}
|
||||
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
return ReadFixedHeaderResult.ConnectionClosed;
|
||||
}
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
|
||||
_packetInspector?.FillReceiveBuffer(buffer);
|
||||
|
||||
var hasRemainingLength = buffer[1] != 0;
|
||||
if (!hasRemainingLength)
|
||||
{
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
FixedHeader = new MqttFixedHeader(buffer[0], 0, totalBytesRead)
|
||||
};
|
||||
}
|
||||
|
||||
var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (bodyLength == -1)
|
||||
{
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
IsConnectionClosed = true
|
||||
};
|
||||
}
|
||||
|
||||
totalBytesRead += bodyLength;
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
FixedHeader = new MqttFixedHeader(buffer[0], bodyLength, totalBytesRead)
|
||||
};
|
||||
}
|
||||
|
||||
async Task<ReceivedMqttPacket> ReceiveAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
return ReceivedMqttPacket.Empty;
|
||||
}
|
||||
|
||||
var readFixedHeaderResult = await ReadFixedHeaderAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
return ReceivedMqttPacket.Empty;
|
||||
}
|
||||
|
||||
if (readFixedHeaderResult.ConnectionClosed)
|
||||
if (readFixedHeaderResult.IsConnectionClosed)
|
||||
{
|
||||
return null;
|
||||
return ReceivedMqttPacket.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -246,7 +343,7 @@ namespace MQTTnet.Adapter
|
||||
var fixedHeader = readFixedHeaderResult.FixedHeader;
|
||||
if (fixedHeader.RemainingLength == 0)
|
||||
{
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, new MqttPacketBodyReader(new byte[0], 0, 0), 2);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, PlatformAbstractionLayer.EmptyByteArraySegment, 2);
|
||||
}
|
||||
|
||||
var bodyLength = fixedHeader.RemainingLength;
|
||||
@@ -267,21 +364,21 @@ namespace MQTTnet.Adapter
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
return ReceivedMqttPacket.Empty;
|
||||
}
|
||||
|
||||
if (readBytes == 0)
|
||||
{
|
||||
return null;
|
||||
return ReceivedMqttPacket.Empty;
|
||||
}
|
||||
|
||||
bodyOffset += readBytes;
|
||||
} while (bodyOffset < bodyLength);
|
||||
|
||||
_packetInspectorHandler.FillReceiveBuffer(body);
|
||||
_packetInspector?.FillReceiveBuffer(body);
|
||||
|
||||
var bodyReader = new MqttPacketBodyReader(body, 0, bodyLength);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, bodyReader, fixedHeader.TotalLength);
|
||||
var bodySegment = new ArraySegment<byte>(body, 0, bodyLength);
|
||||
return new ReceivedMqttPacket(fixedHeader.Flags, bodySegment, fixedHeader.TotalLength);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -289,114 +386,14 @@ namespace MQTTnet.Adapter
|
||||
}
|
||||
}
|
||||
|
||||
async Task<ReadFixedHeaderResult> ReadFixedHeaderAsync(CancellationToken cancellationToken)
|
||||
static bool WrapAndThrowException(Exception exception)
|
||||
{
|
||||
// The MQTT fixed header contains 1 byte of flags and at least 1 byte for the remaining data length.
|
||||
// So in all cases at least 2 bytes must be read for a complete MQTT packet.
|
||||
var buffer = _fixedHeaderBuffer;
|
||||
var totalBytesRead = 0;
|
||||
|
||||
while (totalBytesRead < buffer.Length)
|
||||
if (exception is OperationCanceledException || exception is MqttCommunicationTimedOutException || exception is MqttCommunicationException ||
|
||||
exception is MqttProtocolViolationException)
|
||||
{
|
||||
var bytesRead = await _channel.ReadAsync(buffer, totalBytesRead, buffer.Length - totalBytesRead, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
ConnectionClosed = true
|
||||
};
|
||||
}
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
return false;
|
||||
}
|
||||
|
||||
_packetInspectorHandler.FillReceiveBuffer(buffer);
|
||||
|
||||
var hasRemainingLength = buffer[1] != 0;
|
||||
if (!hasRemainingLength)
|
||||
{
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
FixedHeader = new MqttFixedHeader(buffer[0], 0, totalBytesRead)
|
||||
};
|
||||
}
|
||||
|
||||
var bodyLength = await ReadBodyLengthAsync(buffer[1], cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (!bodyLength.HasValue)
|
||||
{
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
ConnectionClosed = true
|
||||
};
|
||||
}
|
||||
|
||||
totalBytesRead += bodyLength.Value;
|
||||
return new ReadFixedHeaderResult
|
||||
{
|
||||
FixedHeader = new MqttFixedHeader(buffer[0], bodyLength.Value, totalBytesRead)
|
||||
};
|
||||
}
|
||||
|
||||
async Task<int?> ReadBodyLengthAsync(byte initialEncodedByte, CancellationToken cancellationToken)
|
||||
{
|
||||
var offset = 0;
|
||||
var multiplier = 128;
|
||||
var value = initialEncodedByte & 127;
|
||||
int encodedByte = initialEncodedByte;
|
||||
|
||||
while ((encodedByte & 128) != 0)
|
||||
{
|
||||
offset++;
|
||||
if (offset > 3)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Remaining length is invalid.");
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var readCount = await _channel.ReadAsync(_singleByteBuffer, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (readCount == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_packetInspectorHandler.FillReceiveBuffer(_singleByteBuffer);
|
||||
|
||||
encodedByte = _singleByteBuffer[0];
|
||||
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool IsWrappedException(Exception exception)
|
||||
{
|
||||
return exception is OperationCanceledException ||
|
||||
exception is MqttCommunicationTimedOutException ||
|
||||
exception is MqttCommunicationException ||
|
||||
exception is MqttProtocolViolationException;
|
||||
}
|
||||
|
||||
static void WrapAndThrowException(Exception exception)
|
||||
{
|
||||
if (exception is IOException && exception.InnerException is SocketException innerException)
|
||||
{
|
||||
exception = innerException;
|
||||
@@ -426,4 +423,4 @@ namespace MQTTnet.Adapter
|
||||
throw new MqttCommunicationException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
// 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 MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
|
||||
+35
-21
@@ -1,39 +1,48 @@
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Diagnostics.PacketInspection;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Adapter
|
||||
{
|
||||
public sealed class MqttPacketInspectorHandler
|
||||
public sealed class MqttPacketInspector
|
||||
{
|
||||
readonly MemoryStream _receivedPacketBuffer;
|
||||
readonly IMqttPacketInspector _packetInspector;
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
|
||||
public MqttPacketInspectorHandler(IMqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||
readonly AsyncEvent<InspectMqttPacketEventArgs> _asyncEvent;
|
||||
|
||||
MemoryStream _receivedPacketBuffer;
|
||||
|
||||
public MqttPacketInspector(AsyncEvent<InspectMqttPacketEventArgs> asyncEvent, IMqttNetLogger logger)
|
||||
{
|
||||
_packetInspector = packetInspector;
|
||||
|
||||
if (packetInspector != null)
|
||||
{
|
||||
_receivedPacketBuffer = new MemoryStream();
|
||||
}
|
||||
|
||||
_asyncEvent = asyncEvent ?? throw new ArgumentNullException(nameof(asyncEvent));
|
||||
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.WithSource(nameof(MqttPacketInspectorHandler));
|
||||
_logger = logger.WithSource(nameof(MqttPacketInspector));
|
||||
}
|
||||
|
||||
public void BeginReceivePacket()
|
||||
{
|
||||
if (!_asyncEvent.HasHandlers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_receivedPacketBuffer == null)
|
||||
{
|
||||
_receivedPacketBuffer = new MemoryStream();
|
||||
}
|
||||
|
||||
_receivedPacketBuffer?.SetLength(0);
|
||||
}
|
||||
|
||||
public void EndReceivePacket()
|
||||
{
|
||||
if (_packetInspector == null)
|
||||
if (!_asyncEvent.HasHandlers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -44,9 +53,9 @@ namespace MQTTnet.Adapter
|
||||
InspectPacket(buffer, MqttPacketFlowDirection.Inbound);
|
||||
}
|
||||
|
||||
public void BeginSendPacket(ArraySegment<byte> buffer)
|
||||
public void BeginSendPacket(MqttPacketBuffer buffer)
|
||||
{
|
||||
if (_packetInspector == null)
|
||||
if (!_asyncEvent.HasHandlers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -61,6 +70,11 @@ namespace MQTTnet.Adapter
|
||||
|
||||
public void FillReceiveBuffer(byte[] buffer)
|
||||
{
|
||||
if (!_asyncEvent.HasHandlers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_receivedPacketBuffer?.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
@@ -68,13 +82,13 @@ namespace MQTTnet.Adapter
|
||||
{
|
||||
try
|
||||
{
|
||||
var context = new ProcessMqttPacketContext
|
||||
var eventArgs = new InspectMqttPacketEventArgs
|
||||
{
|
||||
Buffer = buffer,
|
||||
Direction = direction
|
||||
};
|
||||
|
||||
_packetInspector.ProcessMqttPacket(context);
|
||||
_asyncEvent.InvokeAsync(eventArgs).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -1,20 +1,25 @@
|
||||
using System;
|
||||
using MQTTnet.Formatter;
|
||||
// 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.Adapter
|
||||
{
|
||||
public sealed class ReceivedMqttPacket
|
||||
public readonly struct ReceivedMqttPacket
|
||||
{
|
||||
public ReceivedMqttPacket(byte fixedHeader, IMqttPacketBodyReader bodyReader, int totalLength)
|
||||
public static readonly ReceivedMqttPacket Empty = new ReceivedMqttPacket();
|
||||
|
||||
public ReceivedMqttPacket(byte fixedHeader, ArraySegment<byte> body, int totalLength)
|
||||
{
|
||||
FixedHeader = fixedHeader;
|
||||
BodyReader = bodyReader ?? throw new ArgumentNullException(nameof(bodyReader));
|
||||
Body = body;
|
||||
TotalLength = totalLength;
|
||||
}
|
||||
|
||||
public byte FixedHeader { get; }
|
||||
|
||||
public IMqttPacketBodyReader BodyReader { get; }
|
||||
public ArraySegment<byte> Body { get; }
|
||||
|
||||
public int TotalLength { get; }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// 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.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Certificates
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
// 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.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Certificates
|
||||
{
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#if !WINDOWS_UWP
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#if !WINDOWS_UWP
|
||||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// 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.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public interface IMqttClientConnectedHandler
|
||||
{
|
||||
Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client.Connecting
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientConnectResult
|
||||
{
|
||||
@@ -83,11 +87,12 @@ namespace MQTTnet.Client.Connecting
|
||||
public string ServerReference { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// MQTTv5 only.
|
||||
/// Gets the keep alive interval which was chosen by the server instead of the
|
||||
/// keep alive interval from the client CONNECT packet.
|
||||
/// MQTTv5 only.
|
||||
/// A value of 0 indicates that the feature is not used.
|
||||
/// </summary>
|
||||
public ushort? ServerKeepAlive { get; internal set; }
|
||||
public ushort ServerKeepAlive { get; internal set; }
|
||||
|
||||
public uint? SessionExpiryInterval { get; internal set; }
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Client.Connecting
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public enum MqttClientConnectResultCode
|
||||
{
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// 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 MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientConnectResultFactory
|
||||
{
|
||||
public MqttClientConnectResult Create(MqttConnAckPacket connAckPacket, MqttProtocolVersion protocolVersion)
|
||||
{
|
||||
if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket));
|
||||
|
||||
if (protocolVersion == MqttProtocolVersion.V500)
|
||||
{
|
||||
return CreateForMqtt500(connAckPacket);
|
||||
}
|
||||
|
||||
return CreateForMqtt311(connAckPacket);
|
||||
}
|
||||
|
||||
static MqttClientConnectResult CreateForMqtt500(MqttConnAckPacket connAckPacket)
|
||||
{
|
||||
if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket));
|
||||
|
||||
return new MqttClientConnectResult
|
||||
{
|
||||
IsSessionPresent = connAckPacket.IsSessionPresent,
|
||||
ResultCode = (MqttClientConnectResultCode) (int) connAckPacket.ReasonCode,
|
||||
WildcardSubscriptionAvailable = connAckPacket.WildcardSubscriptionAvailable,
|
||||
RetainAvailable = connAckPacket.RetainAvailable,
|
||||
AssignedClientIdentifier = connAckPacket.AssignedClientIdentifier,
|
||||
AuthenticationMethod = connAckPacket.AuthenticationMethod,
|
||||
AuthenticationData = connAckPacket.AuthenticationData,
|
||||
MaximumPacketSize = connAckPacket.MaximumPacketSize,
|
||||
ReasonString = connAckPacket.ReasonString,
|
||||
ReceiveMaximum = connAckPacket.ReceiveMaximum,
|
||||
MaximumQoS = connAckPacket.MaximumQoS,
|
||||
ResponseInformation = connAckPacket.ResponseInformation,
|
||||
TopicAliasMaximum = connAckPacket.TopicAliasMaximum,
|
||||
ServerReference = connAckPacket.ServerReference,
|
||||
ServerKeepAlive = connAckPacket.ServerKeepAlive,
|
||||
SessionExpiryInterval = connAckPacket.SessionExpiryInterval,
|
||||
SubscriptionIdentifiersAvailable = connAckPacket.SubscriptionIdentifiersAvailable,
|
||||
SharedSubscriptionAvailable = connAckPacket.SharedSubscriptionAvailable,
|
||||
UserProperties = connAckPacket.UserProperties
|
||||
};
|
||||
}
|
||||
|
||||
static MqttClientConnectResult CreateForMqtt311(MqttConnAckPacket connAckPacket)
|
||||
{
|
||||
if (connAckPacket == null) throw new ArgumentNullException(nameof(connAckPacket));
|
||||
|
||||
return new MqttClientConnectResult
|
||||
{
|
||||
RetainAvailable = true, // Always true because v3.1.1 does not have a way to "disable" that feature.
|
||||
WildcardSubscriptionAvailable = true, // Always true because v3.1.1 does not have a way to "disable" that feature.
|
||||
IsSessionPresent = connAckPacket.IsSessionPresent,
|
||||
ResultCode = ConvertReturnCodeToResultCode(connAckPacket.ReturnCode)
|
||||
};
|
||||
}
|
||||
|
||||
static MqttClientConnectResultCode ConvertReturnCodeToResultCode(MqttConnectReturnCode connectReturnCode)
|
||||
{
|
||||
switch (connectReturnCode)
|
||||
{
|
||||
case MqttConnectReturnCode.ConnectionAccepted:
|
||||
{
|
||||
return MqttClientConnectResultCode.Success;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion:
|
||||
{
|
||||
return MqttClientConnectResultCode.UnsupportedProtocolVersion;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedNotAuthorized:
|
||||
{
|
||||
return MqttClientConnectResultCode.NotAuthorized;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword:
|
||||
{
|
||||
return MqttClientConnectResultCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedIdentifierRejected:
|
||||
{
|
||||
return MqttClientConnectResultCode.ClientIdentifierNotValid;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedServerUnavailable:
|
||||
{
|
||||
return MqttClientConnectResultCode.ServerUnavailable;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new MqttProtocolViolationException("Received unexpected return code.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client.Connecting
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientConnectedEventArgs : EventArgs
|
||||
public sealed class MqttClientConnectedEventArgs : EventArgs
|
||||
{
|
||||
public MqttClientConnectedEventArgs(MqttClientConnectResult connectResult)
|
||||
{
|
||||
@@ -10,9 +14,9 @@ namespace MQTTnet.Client.Connecting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authentication result.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// Gets the authentication result.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientConnectResult ConnectResult { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Client.Connecting
|
||||
{
|
||||
public class MqttClientConnectedHandlerDelegate : IMqttClientConnectedHandler
|
||||
{
|
||||
private readonly Func<MqttClientConnectedEventArgs, Task> _handler;
|
||||
|
||||
public MqttClientConnectedHandlerDelegate(Action<MqttClientConnectedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = context =>
|
||||
{
|
||||
handler(context);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
|
||||
public MqttClientConnectedHandlerDelegate(Func<MqttClientConnectedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
return _handler(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientConnectingEventArgs : EventArgs
|
||||
{
|
||||
public MqttClientConnectingEventArgs(MqttClientOptions clientOptions)
|
||||
{
|
||||
ClientOptions = clientOptions;
|
||||
}
|
||||
|
||||
public MqttClientOptions ClientOptions { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Client.Disconnecting
|
||||
{
|
||||
public interface IMqttClientDisconnectedHandler
|
||||
{
|
||||
Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Client.Disconnecting
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientDisconnectOptions
|
||||
{
|
||||
@@ -6,7 +10,7 @@
|
||||
/// Gets or sets the reason code.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientDisconnectReason ReasonCode { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
|
||||
public MqttClientDisconnectReason Reason { get; set; } = MqttClientDisconnectReason.NormalDisconnection;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason string.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientDisconnectOptionsBuilder
|
||||
{
|
||||
MqttClientDisconnectReason _reason = MqttClientDisconnectReason.NormalDisconnection;
|
||||
string _reasonString;
|
||||
|
||||
public MqttClientDisconnectOptionsBuilder WithReasonString(string value)
|
||||
{
|
||||
_reasonString = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientDisconnectOptionsBuilder WithReason(MqttClientDisconnectReason value)
|
||||
{
|
||||
_reason = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientDisconnectOptions Build()
|
||||
{
|
||||
return new MqttClientDisconnectOptions
|
||||
{
|
||||
Reason = _reason,
|
||||
ReasonString = _reasonString
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Client.Disconnecting
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public enum MqttClientDisconnectReason
|
||||
{
|
||||
|
||||
@@ -1,37 +1,29 @@
|
||||
using System;
|
||||
using MQTTnet.Client.Connecting;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client.Disconnecting
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientDisconnectedEventArgs : EventArgs
|
||||
{
|
||||
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception, MqttClientConnectResult connectResult, MqttClientDisconnectReason reason)
|
||||
{
|
||||
ClientWasConnected = clientWasConnected;
|
||||
Exception = exception;
|
||||
ConnectResult = connectResult;
|
||||
Reason = reason;
|
||||
public bool ClientWasConnected { get; internal set; }
|
||||
|
||||
ReasonCode = reason;
|
||||
}
|
||||
|
||||
public bool ClientWasConnected { get; }
|
||||
|
||||
public Exception Exception { get; }
|
||||
public Exception Exception { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authentication result.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientConnectResult ConnectResult { get; }
|
||||
public MqttClientConnectResult ConnectResult { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientDisconnectReason Reason { get; set; }
|
||||
|
||||
[Obsolete("Please use 'Reason' instead. This property will be removed in the future!")]
|
||||
public MqttClientDisconnectReason ReasonCode { get; set; }
|
||||
public MqttClientDisconnectReason Reason { get; internal set; }
|
||||
|
||||
public string ReasonString { get; internal set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Client.Disconnecting
|
||||
{
|
||||
public class MqttClientDisconnectedHandlerDelegate : IMqttClientDisconnectedHandler
|
||||
{
|
||||
private readonly Func<MqttClientDisconnectedEventArgs, Task> _handler;
|
||||
|
||||
public MqttClientDisconnectedHandlerDelegate(Action<MqttClientDisconnectedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = context =>
|
||||
{
|
||||
handler(context);
|
||||
return Task.FromResult(0);
|
||||
};
|
||||
}
|
||||
|
||||
public MqttClientDisconnectedHandlerDelegate(Func<MqttClientDisconnectedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs)
|
||||
{
|
||||
return _handler(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -1,6 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client.ExtendedAuthenticationExchange
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public interface IMqttExtendedAuthenticationExchangeHandler
|
||||
{
|
||||
|
||||
+12
-8
@@ -1,21 +1,25 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client.ExtendedAuthenticationExchange
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttExtendedAuthenticationExchangeContext
|
||||
{
|
||||
public MqttExtendedAuthenticationExchangeContext(MqttAuthPacket authPacket, IMqttClient client)
|
||||
public MqttExtendedAuthenticationExchangeContext(MqttAuthPacket authPacket, MqttClient client)
|
||||
{
|
||||
if (authPacket == null) throw new ArgumentNullException(nameof(authPacket));
|
||||
|
||||
ReasonCode = authPacket.ReasonCode;
|
||||
ReasonString = authPacket.Properties?.ReasonString;
|
||||
AuthenticationMethod = authPacket.Properties?.AuthenticationMethod;
|
||||
AuthenticationData = authPacket.Properties?.AuthenticationData;
|
||||
UserProperties = authPacket.Properties?.UserProperties;
|
||||
ReasonString = authPacket.ReasonString;
|
||||
AuthenticationMethod = authPacket.AuthenticationMethod;
|
||||
AuthenticationData = authPacket.AuthenticationData;
|
||||
UserProperties = authPacket.UserProperties;
|
||||
|
||||
Client = client ?? throw new ArgumentNullException(nameof(client));
|
||||
}
|
||||
@@ -53,6 +57,6 @@ namespace MQTTnet.Client.ExtendedAuthenticationExchange
|
||||
/// </summary>
|
||||
public List<MqttUserProperty> UserProperties { get; }
|
||||
|
||||
public IMqttClient Client { get; }
|
||||
public MqttClient Client { get; }
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -1,8 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client.ExtendedAuthenticationExchange
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttExtendedAuthenticationExchangeData
|
||||
{
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.ExtendedAuthenticationExchange;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
|
||||
{
|
||||
bool IsConnected { get; }
|
||||
|
||||
IMqttClientOptions Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the connected handler that is fired after the client has connected to the server successfully.
|
||||
/// Hint: Initialize handlers before you connect the client to avoid issues.
|
||||
/// </summary>
|
||||
IMqttClientConnectedHandler ConnectedHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the disconnected handler that is fired after the client has disconnected from the server.
|
||||
/// Hint: Initialize handlers before you connect the client to avoid issues.
|
||||
/// </summary>
|
||||
IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
|
||||
|
||||
Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken);
|
||||
|
||||
Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken);
|
||||
|
||||
Task PingAsync(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sends extended authentication data.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
/// <param name="data">The extended data.</param>
|
||||
/// <param name="cancellationToken">A cancellation token to stop the task.</param>
|
||||
/// <returns>A <see cref="Task"/> representing any asynchronous operation.</returns>
|
||||
Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken);
|
||||
|
||||
Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken);
|
||||
|
||||
Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.LowLevelClient;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public interface IMqttClientFactory
|
||||
{
|
||||
IMqttFactory UseClientAdapterFactory(IMqttClientAdapterFactory clientAdapterFactory);
|
||||
|
||||
ILowLevelMqttClient CreateLowLevelMqttClient();
|
||||
|
||||
ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger);
|
||||
|
||||
ILowLevelMqttClient CreateLowLevelMqttClient(IMqttClientAdapterFactory clientAdapterFactory);
|
||||
|
||||
ILowLevelMqttClient CreateLowLevelMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory clientAdapterFactory);
|
||||
|
||||
IMqttClient CreateMqttClient();
|
||||
|
||||
IMqttClient CreateMqttClient(IMqttNetLogger logger);
|
||||
|
||||
IMqttClient CreateMqttClient(IMqttClientAdapterFactory adapterFactory);
|
||||
|
||||
IMqttClient CreateMqttClient(IMqttNetLogger logger, IMqttClientAdapterFactory adapterFactory);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.ExtendedAuthenticationExchange;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Client.Receiving;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.PacketDispatcher;
|
||||
@@ -16,18 +11,33 @@ using MQTTnet.Protocol;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClient : Disposable, IMqttClient
|
||||
public sealed class MqttClient : Disposable
|
||||
{
|
||||
readonly MqttPacketFactories _packetFactories = new MqttPacketFactories();
|
||||
|
||||
readonly MqttClientPublishResultFactory _clientPublishResultFactory = new MqttClientPublishResultFactory();
|
||||
readonly MqttClientSubscribeResultFactory _clientSubscribeResultFactory = new MqttClientSubscribeResultFactory();
|
||||
readonly MqttClientUnsubscribeResultFactory _clientUnsubscribeResultFactory = new MqttClientUnsubscribeResultFactory();
|
||||
readonly MqttApplicationMessageFactory _applicationMessageFactory = new MqttApplicationMessageFactory();
|
||||
|
||||
readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
|
||||
readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
|
||||
readonly object _disconnectLock = new object();
|
||||
|
||||
readonly AsyncEvent<MqttClientConnectedEventArgs> _connectedEvent = new AsyncEvent<MqttClientConnectedEventArgs>();
|
||||
readonly AsyncEvent<MqttClientConnectingEventArgs> _connectingEvent = new AsyncEvent<MqttClientConnectingEventArgs>();
|
||||
readonly AsyncEvent<MqttClientDisconnectedEventArgs> _disconnectedEvent = new AsyncEvent<MqttClientDisconnectedEventArgs>();
|
||||
readonly AsyncEvent<MqttApplicationMessageReceivedEventArgs> _applicationMessageReceivedEvent = new AsyncEvent<MqttApplicationMessageReceivedEventArgs>();
|
||||
readonly AsyncEvent<InspectMqttPacketEventArgs> _inspectPacketEvent = new AsyncEvent<InspectMqttPacketEventArgs>();
|
||||
|
||||
readonly IMqttClientAdapterFactory _adapterFactory;
|
||||
readonly IMqttNetLogger _rootLogger;
|
||||
readonly MqttNetSourceLogger _logger;
|
||||
|
||||
CancellationTokenSource _backgroundCancellationTokenSource;
|
||||
@@ -44,26 +54,50 @@ namespace MQTTnet.Client
|
||||
MqttClientDisconnectReason _disconnectReason;
|
||||
|
||||
DateTime _lastPacketSentTimestamp;
|
||||
|
||||
string _disconnectReasonString;
|
||||
|
||||
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_adapterFactory = channelFactory ?? throw new ArgumentNullException(nameof(channelFactory));
|
||||
_rootLogger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.WithSource(nameof(MqttClient));
|
||||
}
|
||||
|
||||
public IMqttClientConnectedHandler ConnectedHandler { get; set; }
|
||||
|
||||
public IMqttClientDisconnectedHandler DisconnectedHandler { get; set; }
|
||||
|
||||
public IMqttApplicationMessageReceivedHandler ApplicationMessageReceivedHandler { get; set; }
|
||||
|
||||
|
||||
public event Func<MqttClientConnectedEventArgs, Task> ConnectedAsync
|
||||
{
|
||||
add => _connectedEvent.AddHandler(value);
|
||||
remove => _connectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttClientConnectingEventArgs, Task> ConnectingAsync
|
||||
{
|
||||
add => _connectingEvent.AddHandler(value);
|
||||
remove => _connectingEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttClientDisconnectedEventArgs, Task> DisconnectedAsync
|
||||
{
|
||||
add => _disconnectedEvent.AddHandler(value);
|
||||
remove => _disconnectedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<MqttApplicationMessageReceivedEventArgs, Task> ApplicationMessageReceivedAsync
|
||||
{
|
||||
add => _applicationMessageReceivedEvent.AddHandler(value);
|
||||
remove => _applicationMessageReceivedEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<InspectMqttPacketEventArgs, Task> InspectPackage
|
||||
{
|
||||
add => _inspectPacketEvent.AddHandler(value);
|
||||
remove => _inspectPacketEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public bool IsConnected => (MqttClientConnectionStatus)_connectionStatus == MqttClientConnectionStatus.Connected;
|
||||
|
||||
public IMqttClientOptions Options { get; private set; }
|
||||
public MqttClientOptions Options { get; private set; }
|
||||
|
||||
public async Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken)
|
||||
public async Task<MqttClientConnectResult> ConnectAsync(MqttClientOptions options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
if (options.ChannelOptions == null) throw new ArgumentException("ChannelOptions are not set.");
|
||||
@@ -83,32 +117,45 @@ namespace MQTTnet.Client
|
||||
{
|
||||
Options = options;
|
||||
|
||||
if (_connectingEvent.HasHandlers)
|
||||
{
|
||||
await _connectingEvent.InvokeAsync(new MqttClientConnectingEventArgs(options));
|
||||
}
|
||||
|
||||
_packetIdentifierProvider.Reset();
|
||||
_packetDispatcher.CancelAll();
|
||||
|
||||
_backgroundCancellationTokenSource = new CancellationTokenSource();
|
||||
var backgroundCancellationToken = _backgroundCancellationTokenSource.Token;
|
||||
|
||||
var adapter = _adapterFactory.CreateClientAdapter(options);
|
||||
var adapter = _adapterFactory.CreateClientAdapter(options, new MqttPacketInspector(_inspectPacketEvent, _rootLogger), _rootLogger);
|
||||
_adapter = adapter;
|
||||
|
||||
using (var combined = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken))
|
||||
using (var effectiveCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken))
|
||||
{
|
||||
_logger.Verbose("Trying to connect with server '{0}' (Timeout={1}).", options.ChannelOptions, options.CommunicationTimeout);
|
||||
await adapter.ConnectAsync(options.CommunicationTimeout, combined.Token).ConfigureAwait(false);
|
||||
_logger.Verbose("Trying to connect with server '{0}'.", options.ChannelOptions);
|
||||
await adapter.ConnectAsync(effectiveCancellationToken.Token).ConfigureAwait(false);
|
||||
_logger.Verbose("Connection with server established.");
|
||||
|
||||
_publishPacketReceiverQueue?.Dispose();
|
||||
_publishPacketReceiverQueue = new AsyncQueue<MqttPublishPacket>();
|
||||
_publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken);
|
||||
|
||||
_packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken);
|
||||
|
||||
connectResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false);
|
||||
connectResult = await AuthenticateAsync(options, effectiveCancellationToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_lastPacketSentTimestamp = DateTime.UtcNow;
|
||||
|
||||
if (Options.KeepAlivePeriod != TimeSpan.Zero)
|
||||
var keepAliveInterval = Options.KeepAlivePeriod;
|
||||
if (connectResult.ServerKeepAlive > 0)
|
||||
{
|
||||
_logger.Info($"Using keep alive value ({connectResult.ServerKeepAlive}) sent from the server.");
|
||||
keepAliveInterval = TimeSpan.FromSeconds(connectResult.ServerKeepAlive);
|
||||
}
|
||||
|
||||
if (keepAliveInterval != TimeSpan.Zero)
|
||||
{
|
||||
_keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken);
|
||||
}
|
||||
@@ -117,10 +164,10 @@ namespace MQTTnet.Client
|
||||
|
||||
_logger.Info("Connected.");
|
||||
|
||||
var connectedHandler = ConnectedHandler;
|
||||
if (connectedHandler != null)
|
||||
if (_connectedEvent.HasHandlers)
|
||||
{
|
||||
await connectedHandler.HandleConnectedAsync(new MqttClientConnectedEventArgs(connectResult)).ConfigureAwait(false);
|
||||
var eventArgs = new MqttClientConnectedEventArgs(connectResult);
|
||||
await _connectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return connectResult;
|
||||
@@ -137,7 +184,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken)
|
||||
public async Task DisconnectAsync(MqttClientDisconnectOptions options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (options is null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
@@ -156,7 +203,7 @@ namespace MQTTnet.Client
|
||||
|
||||
if (clientWasConnected)
|
||||
{
|
||||
var disconnectPacket = _adapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(options);
|
||||
var disconnectPacket = _packetFactories.Disconnect.Create(options);
|
||||
await SendAsync(disconnectPacket, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -166,32 +213,31 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
public Task PingAsync(CancellationToken cancellationToken)
|
||||
public Task PingAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return SendAndReceiveAsync<MqttPingRespPacket>(MqttPingReqPacket.Instance, cancellationToken);
|
||||
}
|
||||
|
||||
public Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken)
|
||||
public Task SendExtendedAuthenticationExchangeDataAsync(MqttExtendedAuthenticationExchangeData data, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
|
||||
return SendAsync(new MqttAuthPacket
|
||||
var authPacket = new MqttAuthPacket
|
||||
{
|
||||
Properties = new MqttAuthPacketProperties
|
||||
{
|
||||
// This must always be equal to the value from the CONNECT packet. So we use it here to ensure that.
|
||||
AuthenticationMethod = Options.AuthenticationMethod,
|
||||
AuthenticationData = data.AuthenticationData,
|
||||
ReasonString = data.ReasonString,
|
||||
UserProperties = data.UserProperties
|
||||
}
|
||||
}, cancellationToken);
|
||||
// This must always be equal to the value from the CONNECT packet. So we use it here to ensure that.
|
||||
AuthenticationMethod = Options.AuthenticationMethod,
|
||||
AuthenticationData = data.AuthenticationData,
|
||||
ReasonString = data.ReasonString,
|
||||
UserProperties = data.UserProperties
|
||||
};
|
||||
|
||||
return SendAsync(authPacket, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken)
|
||||
public async Task<MqttClientSubscribeResult> SubscribeAsync(MqttClientSubscribeOptions options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
@@ -203,28 +249,30 @@ namespace MQTTnet.Client
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
|
||||
var subscribePacket = _adapter.PacketFormatterAdapter.DataConverter.CreateSubscribePacket(options);
|
||||
var subscribePacket = _packetFactories.Subscribe.Create(options);
|
||||
subscribePacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||
|
||||
var subAckPacket = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket, cancellationToken).ConfigureAwait(false);
|
||||
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientSubscribeResult(subscribePacket, subAckPacket);
|
||||
|
||||
return _clientSubscribeResultFactory.Create(subscribePacket, subAckPacket);
|
||||
}
|
||||
|
||||
public async Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken)
|
||||
public async Task<MqttClientUnsubscribeResult> UnsubscribeAsync(MqttClientUnsubscribeOptions options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
|
||||
var unsubscribePacket = _adapter.PacketFormatterAdapter.DataConverter.CreateUnsubscribePacket(options);
|
||||
|
||||
var unsubscribePacket = _packetFactories.Unsubscribe.Create(options);
|
||||
unsubscribePacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||
|
||||
var unsubAckPacket = await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket, cancellationToken).ConfigureAwait(false);
|
||||
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientUnsubscribeResult(unsubscribePacket, unsubAckPacket);
|
||||
|
||||
return _clientUnsubscribeResultFactory.Create(unsubscribePacket, unsubAckPacket);
|
||||
}
|
||||
|
||||
public Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken)
|
||||
public Task<MqttClientPublishResult> PublishAsync(MqttApplicationMessage applicationMessage, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -233,7 +281,7 @@ namespace MQTTnet.Client
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
|
||||
var publishPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePublishPacket(applicationMessage);
|
||||
var publishPacket = _packetFactories.Publish.Create(applicationMessage);
|
||||
|
||||
switch (applicationMessage.QualityOfServiceLevel)
|
||||
{
|
||||
@@ -284,18 +332,18 @@ namespace MQTTnet.Client
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
async Task<MqttClientConnectResult> AuthenticateAsync(IMqttChannelAdapter channelAdapter, MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken)
|
||||
async Task<MqttClientConnectResult> AuthenticateAsync(MqttClientOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
MqttClientConnectResult result;
|
||||
|
||||
try
|
||||
{
|
||||
var connectPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket(
|
||||
willApplicationMessage,
|
||||
Options);
|
||||
var connectPacket = _packetFactories.Connect.Create(options);
|
||||
|
||||
var connAckPacket = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false);
|
||||
result = channelAdapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket);
|
||||
|
||||
var clientConnectResultFactory = new MqttClientConnectResultFactory();
|
||||
result = clientConnectResultFactory.Create(connAckPacket, options.ProtocolVersion);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -322,7 +370,10 @@ namespace MQTTnet.Client
|
||||
|
||||
void ThrowIfConnected(string message)
|
||||
{
|
||||
if (IsConnected) throw new MqttProtocolViolationException(message);
|
||||
if (IsConnected)
|
||||
{
|
||||
throw new MqttProtocolViolationException(message);
|
||||
}
|
||||
}
|
||||
|
||||
Task DisconnectInternalAsync(Task sender, Exception exception, MqttClientConnectResult connectResult)
|
||||
@@ -345,8 +396,12 @@ namespace MQTTnet.Client
|
||||
{
|
||||
if (_adapter != null)
|
||||
{
|
||||
_logger.Verbose("Disconnecting [Timeout={0}]", Options.CommunicationTimeout);
|
||||
await _adapter.DisconnectAsync(Options.CommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
|
||||
_logger.Verbose("Disconnecting [Timeout={0}]", Options.Timeout);
|
||||
|
||||
using (var timeout = new CancellationTokenSource(Options.Timeout))
|
||||
{
|
||||
await _adapter.DisconnectAsync(timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Verbose("Disconnected from adapter.");
|
||||
@@ -376,13 +431,18 @@ namespace MQTTnet.Client
|
||||
|
||||
_logger.Info("Disconnected.");
|
||||
|
||||
var disconnectedHandler = DisconnectedHandler;
|
||||
if (disconnectedHandler != null)
|
||||
var eventArgs = new MqttClientDisconnectedEventArgs
|
||||
{
|
||||
// This handler must be executed in a new thread because otherwise a dead lock may happen
|
||||
// when trying to reconnect in that handler etc.
|
||||
Task.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, connectResult, _disconnectReason))).RunInBackground(_logger);
|
||||
}
|
||||
ClientWasConnected = clientWasConnected,
|
||||
Exception = exception,
|
||||
ConnectResult = connectResult,
|
||||
Reason = _disconnectReason,
|
||||
ReasonString = _disconnectReasonString
|
||||
};
|
||||
|
||||
// This handler must be executed in a new thread because otherwise a dead lock may happen
|
||||
// when trying to reconnect in that handler etc.
|
||||
Task.Run(() => _disconnectedEvent.InvokeAsync(eventArgs)).RunInBackground(_logger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,7 +461,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
Task SendAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
Task SendAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -410,17 +470,17 @@ namespace MQTTnet.Client
|
||||
return _adapter.SendPacketAsync(packet, cancellationToken);
|
||||
}
|
||||
|
||||
async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttBasePacket
|
||||
async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttPacket requestPacket, CancellationToken cancellationToken) where TResponsePacket : MqttPacket
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
ushort packetIdentifier = 0;
|
||||
if (requestPacket is IMqttPacketWithIdentifier packetWithIdentifier)
|
||||
if (requestPacket is MqttPacketWithIdentifier packetWithIdentifier)
|
||||
{
|
||||
packetIdentifier = packetWithIdentifier.PacketIdentifier;
|
||||
}
|
||||
|
||||
using (var packetAwaiter = _packetDispatcher.AddAwaitable<TResponsePacket>(packetIdentifier))
|
||||
using (var packetAwaitable = _packetDispatcher.AddAwaitable<TResponsePacket>(packetIdentifier))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -429,12 +489,12 @@ namespace MQTTnet.Client
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Warning(exception, "Error when sending request packet ({0}).", requestPacket.GetType().Name);
|
||||
packetAwaiter.Fail(exception);
|
||||
packetAwaitable.Fail(exception);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return await packetAwaiter.WaitOneAsync(Options.CommunicationTimeout).ConfigureAwait(false);
|
||||
return await packetAwaitable.WaitOneAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -509,7 +569,17 @@ namespace MQTTnet.Client
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var packet = await _adapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false);
|
||||
MqttPacket packet;
|
||||
var packetTask = _adapter.ReceivePacketAsync(cancellationToken);
|
||||
|
||||
if (packetTask.IsCompleted)
|
||||
{
|
||||
packet = packetTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet = await packetTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -555,7 +625,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
async Task TryProcessReceivedPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
async Task TryProcessReceivedPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -672,7 +742,7 @@ namespace MQTTnet.Client
|
||||
{
|
||||
if (!eventArgs.ProcessingFailed)
|
||||
{
|
||||
var pubAckPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubAckPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
|
||||
var pubAckPacket = _packetFactories.PubAck.Create(eventArgs);
|
||||
return SendAsync(pubAckPacket, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -680,7 +750,7 @@ namespace MQTTnet.Client
|
||||
{
|
||||
if (!eventArgs.ProcessingFailed)
|
||||
{
|
||||
var pubRecPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRecPacket(eventArgs.PublishPacket, eventArgs.ReasonCode);
|
||||
var pubRecPacket = _packetFactories.PubRec.Create(eventArgs);
|
||||
return SendAsync(pubRecPacket, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -698,7 +768,7 @@ namespace MQTTnet.Client
|
||||
{
|
||||
// The packet is unknown. Probably due to a restart of the client.
|
||||
// So wen send this to the server to trigger a full resend of the message.
|
||||
var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound);
|
||||
var pubRelPacket = _packetFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.PacketIdentifierNotFound);
|
||||
return SendAsync(pubRelPacket, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -707,14 +777,15 @@ namespace MQTTnet.Client
|
||||
|
||||
Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
var pubCompPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubCompPacket(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||
var pubCompPacket = _packetFactories.PubComp.Create(pubRelPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||
return SendAsync(pubCompPacket, cancellationToken);
|
||||
}
|
||||
|
||||
Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket)
|
||||
{
|
||||
_disconnectReason = (MqttClientDisconnectReason) (disconnectPacket.ReasonCode ?? MqttDisconnectReasonCode.NormalDisconnection);
|
||||
|
||||
_disconnectReason = (MqttClientDisconnectReason) disconnectPacket.ReasonCode;
|
||||
_disconnectReasonString = disconnectPacket.ReasonString;
|
||||
|
||||
// Also dispatch disconnect to waiting threads to generate a proper exception.
|
||||
_packetDispatcher.FailAll(new MqttUnexpectedDisconnectReceivedException(disconnectPacket));
|
||||
|
||||
@@ -735,8 +806,10 @@ namespace MQTTnet.Client
|
||||
async Task<MqttClientPublishResult> PublishAtMostOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
|
||||
await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(null);
|
||||
await SendAsync(publishPacket, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return _clientPublishResultFactory.Create(null);
|
||||
}
|
||||
|
||||
async Task<MqttClientPublishResult> PublishAtLeastOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
@@ -744,8 +817,8 @@ namespace MQTTnet.Client
|
||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
|
||||
|
||||
var pubAckPacket = await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubAckPacket);
|
||||
|
||||
return _clientPublishResultFactory.Create(pubAckPacket);
|
||||
}
|
||||
|
||||
async Task<MqttClientPublishResult> PublishExactlyOnceAsync(MqttPublishPacket publishPacket, CancellationToken cancellationToken)
|
||||
@@ -754,23 +827,19 @@ namespace MQTTnet.Client
|
||||
|
||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var pubRelPacket = _adapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||
var pubRelPacket = _packetFactories.PubRel.Create(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
|
||||
|
||||
var pubCompPacket = await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return _adapter.PacketFormatterAdapter.DataConverter.CreateClientPublishResult(pubRecPacket, pubCompPacket);
|
||||
return _clientPublishResultFactory.Create(pubRecPacket, pubCompPacket);
|
||||
}
|
||||
|
||||
async Task<MqttApplicationMessageReceivedEventArgs> HandleReceivedApplicationMessageAsync(MqttPublishPacket publishPacket)
|
||||
{
|
||||
var applicationMessage = _adapter.PacketFormatterAdapter.DataConverter.CreateApplicationMessage(publishPacket);
|
||||
var applicationMessage = _applicationMessageFactory.Create(publishPacket);
|
||||
var eventArgs = new MqttApplicationMessageReceivedEventArgs(Options.ClientId, applicationMessage, publishPacket, AcknowledgeReceivedPublishPacket);
|
||||
|
||||
var handler = ApplicationMessageReceivedHandler;
|
||||
if (handler != null)
|
||||
{
|
||||
await handler.HandleApplicationMessageReceivedAsync(eventArgs).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await _applicationMessageReceivedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
|
||||
|
||||
return eventArgs;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public enum MqttClientConnectionStatus
|
||||
|
||||
@@ -1,314 +1,140 @@
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.ExtendedAuthenticationExchange;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Client.Receiving;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Protocol;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public static class MqttClientExtensions
|
||||
{
|
||||
public static IMqttClient UseConnectedHandler(this IMqttClient client, Func<MqttClientConnectedEventArgs, Task> handler)
|
||||
public static Task DisconnectAsync(this MqttClient client, MqttClientDisconnectReason reason = MqttClientDisconnectReason.NormalDisconnection, string reasonString = null)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
if (client == null)
|
||||
{
|
||||
return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
|
||||
throw new ArgumentNullException(nameof(client));
|
||||
}
|
||||
|
||||
return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
|
||||
return client.DisconnectAsync(
|
||||
new MqttClientDisconnectOptions
|
||||
{
|
||||
Reason = reason,
|
||||
ReasonString = reasonString
|
||||
},
|
||||
CancellationToken.None);
|
||||
}
|
||||
|
||||
public static IMqttClient UseConnectedHandler(this IMqttClient client, Action<MqttClientConnectedEventArgs> handler)
|
||||
public static Task<MqttClientPublishResult> PublishBinaryAsync(
|
||||
this MqttClient mqttClient,
|
||||
string topic,
|
||||
IEnumerable<byte> payload = null,
|
||||
MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
|
||||
bool retain = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
if (mqttClient == null)
|
||||
{
|
||||
return client.UseConnectedHandler((IMqttClientConnectedHandler)null);
|
||||
throw new ArgumentNullException(nameof(mqttClient));
|
||||
}
|
||||
|
||||
return client.UseConnectedHandler(new MqttClientConnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttClient UseConnectedHandler(this IMqttClient client, IMqttClientConnectedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.ConnectedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static IMqttClient UseDisconnectedHandler(this IMqttClient client, Func<MqttClientDisconnectedEventArgs, Task> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
if (topic == null)
|
||||
{
|
||||
return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
|
||||
throw new ArgumentNullException(nameof(topic));
|
||||
}
|
||||
|
||||
return client.UseDisconnectedHandler(new MqttClientDisconnectedHandlerDelegate(handler));
|
||||
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithRetainFlag(retain)
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
||||
.Build();
|
||||
|
||||
return mqttClient.PublishAsync(applicationMessage, cancellationToken);
|
||||
}
|
||||
|
||||
public static IMqttClient UseDisconnectedHandler(this IMqttClient client, Action<MqttClientDisconnectedEventArgs> handler)
|
||||
public static Task<MqttClientPublishResult> PublishStringAsync(
|
||||
this MqttClient mqttClient,
|
||||
string topic,
|
||||
string payload = null,
|
||||
MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
|
||||
bool retain = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
var payloadBuffer = Encoding.UTF8.GetBytes(payload ?? string.Empty);
|
||||
return mqttClient.PublishBinaryAsync(topic, payloadBuffer, qualityOfServiceLevel, retain, cancellationToken);
|
||||
}
|
||||
|
||||
if (handler == null)
|
||||
public static Task SendExtendedAuthenticationExchangeDataAsync(this MqttClient client, MqttExtendedAuthenticationExchangeData data)
|
||||
{
|
||||
if (client == null)
|
||||
{
|
||||
return client.UseDisconnectedHandler((IMqttClientDisconnectedHandler)null);
|
||||
throw new ArgumentNullException(nameof(client));
|
||||
}
|
||||
|
||||
return client.UseDisconnectedHandler(new MqttClientDisconnectedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttClient UseDisconnectedHandler(this IMqttClient client, IMqttClientDisconnectedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.DisconnectedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static IMqttClient UseApplicationMessageReceivedHandler(this IMqttClient client, Func<MqttApplicationMessageReceivedEventArgs, Task> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
return client.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttClient UseApplicationMessageReceivedHandler(this IMqttClient client, Action<MqttApplicationMessageReceivedEventArgs> handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
return client.UseApplicationMessageReceivedHandler((IMqttApplicationMessageReceivedHandler)null);
|
||||
}
|
||||
|
||||
return client.UseApplicationMessageReceivedHandler(new MqttApplicationMessageReceivedHandlerDelegate(handler));
|
||||
}
|
||||
|
||||
public static IMqttClient UseApplicationMessageReceivedHandler(this IMqttClient client, IMqttApplicationMessageReceivedHandler handler)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
client.ApplicationMessageReceivedHandler = handler;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static Task<MqttClientConnectResult> ReconnectAsync(this IMqttClient client)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (client.Options == null)
|
||||
{
|
||||
throw new InvalidOperationException("_ReconnectAsync_ can be used only if _ConnectAsync_ was called before.");
|
||||
}
|
||||
|
||||
return client.ConnectAsync(client.Options);
|
||||
}
|
||||
|
||||
public static Task<MqttClientConnectResult> ReconnectAsync(this IMqttClient client, CancellationToken cancellationToken)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
if (client.Options == null)
|
||||
{
|
||||
throw new InvalidOperationException("_ReconnectAsync_ can be used only if _ConnectAsync_ was called before.");
|
||||
}
|
||||
|
||||
return client.ConnectAsync(client.Options, cancellationToken);
|
||||
}
|
||||
|
||||
public static Task DisconnectAsync(this IMqttClient client)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return client.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
public static Task DisconnectAsync(this IMqttClient client, MqttClientDisconnectOptions options)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return client.DisconnectAsync(options, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static Task DisconnectAsync(this IMqttClient client, CancellationToken cancellationToken)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return client.DisconnectAsync(new MqttClientDisconnectOptions(), cancellationToken);
|
||||
}
|
||||
|
||||
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, params MqttTopicFilter[] topicFilters)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
var options = new MqttClientSubscribeOptions();
|
||||
options.TopicFilters.AddRange(topicFilters);
|
||||
|
||||
return client.SubscribeAsync(options);
|
||||
}
|
||||
|
||||
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
||||
}
|
||||
|
||||
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, string topic)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(topic).Build());
|
||||
}
|
||||
|
||||
public static Task<MqttClientUnsubscribeResult> UnsubscribeAsync(this IMqttClient client, params string[] topicFilters)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
var options = new MqttClientUnsubscribeOptions();
|
||||
options.TopicFilters.AddRange(topicFilters);
|
||||
|
||||
return client.UnsubscribeAsync(options);
|
||||
}
|
||||
|
||||
public static Task<MqttClientConnectResult> ConnectAsync(this IMqttClient client, IMqttClientOptions options)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return client.ConnectAsync(options, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static Task SendExtendedAuthenticationExchangeDataAsync(this IMqttClient client, MqttExtendedAuthenticationExchangeData data)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return client.SendExtendedAuthenticationExchangeDataAsync(data, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static Task<MqttClientSubscribeResult> SubscribeAsync(this IMqttClient client, MqttClientSubscribeOptions options)
|
||||
public static Task<MqttClientSubscribeResult> SubscribeAsync(this MqttClient mqttClient, MqttTopicFilter topicFilter, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return client.SubscribeAsync(options, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static Task<MqttClientUnsubscribeResult> UnsubscribeAsync(this IMqttClient client, MqttClientUnsubscribeOptions options)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
|
||||
return client.UnsubscribeAsync(options, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IMqttClient client, MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
return client.PublishAsync(applicationMessage, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task PublishAsync(this IMqttClient client, IEnumerable<MqttApplicationMessage> applicationMessages)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||
|
||||
foreach (var applicationMessage in applicationMessages)
|
||||
if (mqttClient == null)
|
||||
{
|
||||
await client.PublishAsync(applicationMessage).ConfigureAwait(false);
|
||||
throw new ArgumentNullException(nameof(mqttClient));
|
||||
}
|
||||
|
||||
if (topicFilter == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(topicFilter));
|
||||
}
|
||||
|
||||
var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(topicFilter)
|
||||
.Build();
|
||||
|
||||
return mqttClient.SubscribeAsync(subscribeOptions, cancellationToken);
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IMqttClient client, string topic)
|
||||
public static Task<MqttClientSubscribeResult> SubscribeAsync(
|
||||
this MqttClient mqttClient,
|
||||
string topic,
|
||||
MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
if (mqttClient == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mqttClient));
|
||||
}
|
||||
|
||||
return client.PublishAsync(new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topic)
|
||||
.Build());
|
||||
if (topic == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(topic));
|
||||
}
|
||||
|
||||
var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(topic, qualityOfServiceLevel)
|
||||
.Build();
|
||||
|
||||
return mqttClient.SubscribeAsync(subscribeOptions, cancellationToken);
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IMqttClient client, string topic, IEnumerable<byte> payload)
|
||||
public static Task<MqttClientUnsubscribeResult> UnsubscribeAsync(this MqttClient mqttClient, string topic, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
if (mqttClient == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mqttClient));
|
||||
}
|
||||
|
||||
return client.PublishAsync(new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.Build());
|
||||
}
|
||||
if (topic == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(topic));
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IMqttClient client, string topic, string payload)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
var unsubscribeOptions = new MqttClientUnsubscribeOptionsBuilder().WithTopicFilter(topic)
|
||||
.Build();
|
||||
|
||||
return client.PublishAsync(new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.Build());
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IMqttClient client, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
||||
.Build());
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IMqttClient client, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
||||
.WithRetainFlag(retain)
|
||||
.Build());
|
||||
}
|
||||
|
||||
public static Task<MqttClientPublishResult> PublishAsync(this IMqttClient client, string topic, string payload, bool retain)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return client.PublishAsync(new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topic)
|
||||
.WithPayload(payload)
|
||||
.WithRetainFlag(retain)
|
||||
.Build());
|
||||
return mqttClient.UnsubscribeAsync(unsubscribeOptions, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Client
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttPacketIdentifierProvider
|
||||
{
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Client.Options
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public interface IMqttClientChannelOptions
|
||||
{
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public interface IMqttClientCredentials
|
||||
{
|
||||
string Username { get; }
|
||||
byte[] Password { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public interface IMqttClientCredentialsProvider
|
||||
{
|
||||
string GetUserName(MqttClientOptions clientOptions);
|
||||
|
||||
byte[] GetPassword(MqttClientOptions clientOptions);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
using MQTTnet.Client.ExtendedAuthenticationExchange;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Diagnostics.PacketInspection;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public interface IMqttClientOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the client identifier.
|
||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||
/// </summary>
|
||||
string ClientId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether clean sessions are used or not.
|
||||
/// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a persistent connection.
|
||||
/// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for the client.
|
||||
/// This mode is ideal when the client only publishes messages.
|
||||
/// It can also connect as a durable client using a persistent connection.
|
||||
/// In this mode, the broker will store subscription information, and undelivered messages for the client.
|
||||
/// </summary>
|
||||
bool CleanSession { get; }
|
||||
|
||||
IMqttClientCredentials Credentials { get; }
|
||||
|
||||
IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; }
|
||||
|
||||
MqttProtocolVersion ProtocolVersion { get; }
|
||||
|
||||
IMqttClientChannelOptions ChannelOptions { get; }
|
||||
|
||||
TimeSpan CommunicationTimeout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keep alive period.
|
||||
/// The connection is normally left open by the client so that is can send and receive data at any time.
|
||||
/// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and expect to receive a PINGRESP from the broker.
|
||||
/// This message exchange confirms that the connection is open and working.
|
||||
/// This period is known as the keep alive period.
|
||||
/// </summary>
|
||||
TimeSpan KeepAlivePeriod { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last will message.
|
||||
/// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client.
|
||||
/// </summary>
|
||||
MqttApplicationMessage WillMessage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the will delay interval.
|
||||
/// This is the time between the client disconnect and the time the will message will be sent.
|
||||
/// </summary>
|
||||
uint? WillDelayInterval { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authentication method.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
string AuthenticationMethod { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authentication data.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
byte[] AuthenticationData { get; }
|
||||
|
||||
uint? MaximumPacketSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the receive maximum.
|
||||
/// This gives the maximum length of the receive messages.
|
||||
/// </summary>
|
||||
ushort? ReceiveMaximum { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request problem information.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
bool? RequestProblemInformation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request response information.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
bool? RequestResponseInformation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the session expiry interval.
|
||||
/// The time after a session expires when it's not actively used.
|
||||
/// </summary>
|
||||
uint? SessionExpiryInterval { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the topic alias maximum.
|
||||
/// This gives the maximum length of the topic alias.
|
||||
/// </summary>
|
||||
ushort? TopicAliasMaximum { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user properties.
|
||||
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
|
||||
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
||||
/// The feature is very similar to the HTTP header concept.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
List<MqttUserProperty> UserProperties { get; set; }
|
||||
|
||||
IMqttPacketInspector PacketInspector { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientCertificateValidationCallbackContext
|
||||
{
|
||||
public X509Certificate Certificate { get; set; }
|
||||
|
||||
public X509Chain Chain { get; set; }
|
||||
|
||||
public SslPolicyErrors SslPolicyErrors { get; set; }
|
||||
|
||||
public IMqttClientChannelOptions ClientOptions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientCertificateValidationEventArgs : EventArgs
|
||||
{
|
||||
public X509Certificate Certificate { get; set; }
|
||||
|
||||
public X509Chain Chain { get; set; }
|
||||
|
||||
public SslPolicyErrors SslPolicyErrors { get; set; }
|
||||
|
||||
public IMqttClientChannelOptions ClientOptions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,28 @@
|
||||
namespace MQTTnet.Client.Options
|
||||
{
|
||||
public class MqttClientCredentials : IMqttClientCredentials
|
||||
{
|
||||
public string Username { get; set; }
|
||||
// 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.
|
||||
|
||||
public byte[] Password { get; set; }
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientCredentials : IMqttClientCredentialsProvider
|
||||
{
|
||||
readonly string _userName;
|
||||
readonly byte[] _password;
|
||||
|
||||
public MqttClientCredentials(string userName, byte[] password = null)
|
||||
{
|
||||
_userName = userName;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
public string GetUserName(MqttClientOptions clientOptions)
|
||||
{
|
||||
return _userName;
|
||||
}
|
||||
|
||||
public byte[] GetPassword(MqttClientOptions clientOptions)
|
||||
{
|
||||
return _password;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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.Linq;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientDefaultCertificateValidationHandler
|
||||
{
|
||||
public static bool Handle(MqttClientCertificateValidationEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.SslPolicyErrors == SslPolicyErrors.None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (eventArgs.Chain.ChainStatus.Any(c =>
|
||||
c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.OfflineRevocation))
|
||||
{
|
||||
if (eventArgs.ClientOptions?.TlsOptions?.IgnoreCertificateRevocationErrors != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (eventArgs.Chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain))
|
||||
{
|
||||
if (eventArgs.ClientOptions?.TlsOptions?.IgnoreCertificateChainErrors != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return eventArgs.ClientOptions?.TlsOptions?.AllowUntrustedCertificates == true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,114 +1,182 @@
|
||||
using MQTTnet.Client.ExtendedAuthenticationExchange;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Diagnostics.PacketInspection;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientOptions : IMqttClientOptions
|
||||
public sealed class MqttClientOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the client identifier.
|
||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||
/// </summary>
|
||||
public string ClientId { get; set; } = Guid.NewGuid().ToString("N");
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether clean sessions are used or not.
|
||||
/// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a persistent connection.
|
||||
/// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for the client.
|
||||
/// This mode is ideal when the client only publishes messages.
|
||||
/// It can also connect as a durable client using a persistent connection.
|
||||
/// In this mode, the broker will store subscription information, and undelivered messages for the client.
|
||||
/// </summary>
|
||||
public bool CleanSession { get; set; } = true;
|
||||
|
||||
public IMqttClientCredentials Credentials { get; set; }
|
||||
|
||||
public IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; set; }
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
|
||||
|
||||
public IMqttClientChannelOptions ChannelOptions { get; set; }
|
||||
|
||||
public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the keep alive period.
|
||||
/// The connection is normally left open by the client so that is can send and receive data at any time.
|
||||
/// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and expect to receive a PINGRESP from the broker.
|
||||
/// This message exchange confirms that the connection is open and working.
|
||||
/// This period is known as the keep alive period.
|
||||
/// </summary>
|
||||
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last will message.
|
||||
/// In MQTT, you use the last will message feature to notify other clients about an ungracefully disconnected client.
|
||||
/// </summary>
|
||||
public MqttApplicationMessage WillMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the will delay interval.
|
||||
/// This is the time between the client disconnect and the time the will message will be sent.
|
||||
/// </summary>
|
||||
public uint? WillDelayInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the authentication method.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public string AuthenticationMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the authentication data.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// Gets or sets the authentication data.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public byte[] AuthenticationData { get; set; }
|
||||
|
||||
public uint? MaximumPacketSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the receive maximum.
|
||||
/// This gives the maximum length of the receive messages.
|
||||
/// Gets or sets the authentication method.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public ushort? ReceiveMaximum { get; set; }
|
||||
public string AuthenticationMethod { get; set; }
|
||||
|
||||
public IMqttClientChannelOptions ChannelOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request problem information.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// Gets or sets a value indicating whether clean sessions are used or not.
|
||||
/// When a client connects to a broker it can connect using either a non persistent connection (clean session) or a
|
||||
/// persistent connection.
|
||||
/// With a non persistent connection the broker doesn't store any subscription information or undelivered messages for
|
||||
/// the client.
|
||||
/// This mode is ideal when the client only publishes messages.
|
||||
/// It can also connect as a durable client using a persistent connection.
|
||||
/// In this mode, the broker will store subscription information, and undelivered messages for the client.
|
||||
/// </summary>
|
||||
public bool? RequestProblemInformation { get; set; }
|
||||
public bool CleanSession { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request response information.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// Gets the client identifier.
|
||||
/// Hint: This identifier needs to be unique over all used clients / devices on the broker to avoid connection issues.
|
||||
/// </summary>
|
||||
public bool? RequestResponseInformation { get; set; }
|
||||
public string ClientId { get; set; } = Guid.NewGuid().ToString("N");
|
||||
|
||||
public IMqttClientCredentialsProvider Credentials { get; set; }
|
||||
|
||||
public IMqttExtendedAuthenticationExchangeHandler ExtendedAuthenticationExchangeHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the session expiry interval.
|
||||
/// The time after a session expires when it's not actively used.
|
||||
/// Gets or sets the keep alive period.
|
||||
/// The connection is normally left open by the client so that is can send and receive data at any time.
|
||||
/// If no data flows over an open connection for a certain time period then the client will generate a PINGREQ and
|
||||
/// expect to receive a PINGRESP from the broker.
|
||||
/// This message exchange confirms that the connection is open and working.
|
||||
/// This period is known as the keep alive period.
|
||||
/// </summary>
|
||||
public uint? SessionExpiryInterval { get; set; }
|
||||
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15);
|
||||
|
||||
public uint MaximumPacketSize { get; set; }
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the topic alias maximum.
|
||||
/// This gives the maximum length of the topic alias.
|
||||
/// Gets or sets the receive maximum.
|
||||
/// This gives the maximum length of the receive messages.
|
||||
/// </summary>
|
||||
public ushort? TopicAliasMaximum { get; set; }
|
||||
public ushort ReceiveMaximum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user properties.
|
||||
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT packet.
|
||||
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
||||
/// The feature is very similar to the HTTP header concept.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// Gets or sets the request problem information.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public bool RequestProblemInformation { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request response information.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public bool RequestResponseInformation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the session expiry interval.
|
||||
/// The time after a session expires when it's not actively used.
|
||||
/// </summary>
|
||||
public uint SessionExpiryInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timeout which will be applied at socket level and internal operations.
|
||||
/// The default value is the same as for sockets in .NET in general.
|
||||
/// </summary>
|
||||
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(100);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the topic alias maximum.
|
||||
/// This gives the maximum length of the topic alias.
|
||||
/// </summary>
|
||||
public ushort TopicAliasMaximum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user properties.
|
||||
/// In MQTT 5, user properties are basic UTF-8 string key-value pairs that you can append to almost every type of MQTT
|
||||
/// packet.
|
||||
/// As long as you don’t exceed the maximum message size, you can use an unlimited number of user properties to add
|
||||
/// metadata to MQTT messages and pass information between publisher, broker, and subscriber.
|
||||
/// The feature is very similar to the HTTP header concept.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public List<MqttUserProperty> UserProperties { get; set; }
|
||||
|
||||
public IMqttPacketInspector PacketInspector { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the content type of the will message.
|
||||
/// </summary>
|
||||
public string WillContentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the correlation data of the will message.
|
||||
/// </summary>
|
||||
public byte[] WillCorrelationData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the will delay interval.
|
||||
/// This is the time between the client disconnect and the time the will message will be sent.
|
||||
/// </summary>
|
||||
public uint WillDelayInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the message expiry interval of the will message.
|
||||
/// </summary>
|
||||
public uint WillMessageExpiryInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the payload of the will message.
|
||||
/// </summary>
|
||||
public byte[] WillPayload { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the payload format indicator of the will message.
|
||||
/// </summary>
|
||||
public MqttPayloadFormatIndicator WillPayloadFormatIndicator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the QoS level of the will message.
|
||||
/// </summary>
|
||||
public MqttQualityOfServiceLevel WillQualityOfServiceLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the response topic of the will message.
|
||||
/// </summary>
|
||||
public string WillResponseTopic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the retain flag of the will message.
|
||||
/// </summary>
|
||||
public bool WillRetain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the topic of the will message.
|
||||
/// </summary>
|
||||
public string WillTopic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user properties of the will message.
|
||||
/// </summary>
|
||||
public List<MqttUserProperty> WillUserProperties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default and initial size of the packet write buffer.
|
||||
/// It is recommended to set this to a value close to the usual expected packet size * 1.5.
|
||||
/// Do not change this value when no memory issues are experienced.
|
||||
/// </summary>
|
||||
public int WriterBufferSize { get; set; } = 4096;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum size of the buffer writer. The writer will reduce its internal buffer
|
||||
/// to this value after serializing a packet.
|
||||
/// Do not change this value when no memory issues are experienced.
|
||||
/// </summary>
|
||||
public int WriterBufferSizeMax { get; set; } = 65535;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,283 +1,27 @@
|
||||
using MQTTnet.Client.ExtendedAuthenticationExchange;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MQTTnet.Diagnostics.PacketInspection;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientOptionsBuilder
|
||||
public sealed class MqttClientOptionsBuilder
|
||||
{
|
||||
readonly MqttClientOptions _options = new MqttClientOptions();
|
||||
|
||||
MqttClientTcpOptions _tcpOptions;
|
||||
MqttClientWebSocketOptions _webSocketOptions;
|
||||
MqttClientOptionsBuilderTlsParameters _tlsParameters;
|
||||
MqttClientWebSocketProxyOptions _proxyOptions;
|
||||
|
||||
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
|
||||
{
|
||||
if (value == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
throw new ArgumentException("Protocol version is invalid.");
|
||||
}
|
||||
MqttClientTcpOptions _tcpOptions;
|
||||
MqttClientOptionsBuilderTlsParameters _tlsParameters;
|
||||
MqttClientWebSocketOptions _webSocketOptions;
|
||||
|
||||
_options.ProtocolVersion = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCommunicationTimeout(TimeSpan value)
|
||||
{
|
||||
_options.CommunicationTimeout = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCleanSession(bool value = true)
|
||||
{
|
||||
_options.CleanSession = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
[Obsolete("This method is no longer supported. The client will send ping requests just before the keep alive interval is going to elapse. As per MQTT RFC the serve has to wait 1.5 times the interval so we don't need this anymore.")]
|
||||
public MqttClientOptionsBuilder WithKeepAliveSendInterval(TimeSpan value)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithNoKeepAlive()
|
||||
{
|
||||
return WithKeepAlivePeriod(TimeSpan.Zero);
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithKeepAlivePeriod(TimeSpan value)
|
||||
{
|
||||
_options.KeepAlivePeriod = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithClientId(string value)
|
||||
{
|
||||
_options.ClientId = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillMessage(MqttApplicationMessage value)
|
||||
{
|
||||
_options.WillMessage = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithAuthentication(string method, byte[] data)
|
||||
{
|
||||
_options.AuthenticationMethod = method;
|
||||
_options.AuthenticationData = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillDelayInterval(uint? willDelayInterval)
|
||||
{
|
||||
_options.WillDelayInterval = willDelayInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTopicAliasMaximum(ushort? topicAliasMaximum)
|
||||
{
|
||||
_options.TopicAliasMaximum = topicAliasMaximum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithMaximumPacketSize(uint? maximumPacketSize)
|
||||
{
|
||||
_options.MaximumPacketSize = maximumPacketSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithReceiveMaximum(ushort? receiveMaximum)
|
||||
{
|
||||
_options.ReceiveMaximum = receiveMaximum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithRequestProblemInformation(bool? requestProblemInformation = true)
|
||||
{
|
||||
_options.RequestProblemInformation = requestProblemInformation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithRequestResponseInformation(bool? requestResponseInformation = true)
|
||||
{
|
||||
_options.RequestResponseInformation = requestResponseInformation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithSessionExpiryInterval(uint? sessionExpiryInterval)
|
||||
{
|
||||
_options.SessionExpiryInterval = sessionExpiryInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithUserProperty(string name, string value)
|
||||
{
|
||||
if (name is null) throw new ArgumentNullException(nameof(name));
|
||||
if (value is null) throw new ArgumentNullException(nameof(value));
|
||||
|
||||
if (_options.UserProperties == null)
|
||||
{
|
||||
_options.UserProperties = new List<MqttUserProperty>();
|
||||
}
|
||||
|
||||
_options.UserProperties.Add(new MqttUserProperty(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCredentials(string username, string password)
|
||||
{
|
||||
byte[] passwordBuffer = null;
|
||||
|
||||
if (password != null)
|
||||
{
|
||||
passwordBuffer = Encoding.UTF8.GetBytes(password);
|
||||
}
|
||||
|
||||
return WithCredentials(username, passwordBuffer);
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCredentials(string username, byte[] password)
|
||||
{
|
||||
_options.Credentials = new MqttClientCredentials
|
||||
{
|
||||
Username = username,
|
||||
Password = password
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCredentials(string username)
|
||||
{
|
||||
_options.Credentials = new MqttClientCredentials
|
||||
{
|
||||
Username = username
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCredentials(IMqttClientCredentials credentials)
|
||||
{
|
||||
_options.Credentials = credentials;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithExtendedAuthenticationExchangeHandler(IMqttExtendedAuthenticationExchangeHandler handler)
|
||||
{
|
||||
_options.ExtendedAuthenticationExchangeHandler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTcpServer(string server, int? port = null)
|
||||
{
|
||||
_tcpOptions = new MqttClientTcpOptions
|
||||
{
|
||||
Server = server,
|
||||
Port = port
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO: Consider creating _MqttClientTcpOptionsBuilder_ as overload.
|
||||
public MqttClientOptionsBuilder WithTcpServer(Action<MqttClientTcpOptions> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
|
||||
|
||||
_tcpOptions = new MqttClientTcpOptions();
|
||||
optionsBuilder.Invoke(_tcpOptions);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithProxy(string address, string username = null, string password = null, string domain = null, bool bypassOnLocal = false, string[] bypassList = null)
|
||||
{
|
||||
_proxyOptions = new MqttClientWebSocketProxyOptions
|
||||
{
|
||||
Address = address,
|
||||
Username = username,
|
||||
Password = password,
|
||||
Domain = domain,
|
||||
BypassOnLocal = bypassOnLocal,
|
||||
BypassList = bypassList
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithProxy(Action<MqttClientWebSocketProxyOptions> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
|
||||
|
||||
_proxyOptions = new MqttClientWebSocketProxyOptions();
|
||||
optionsBuilder(_proxyOptions);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(string uri, MqttClientOptionsBuilderWebSocketParameters parameters = null)
|
||||
{
|
||||
_webSocketOptions = new MqttClientWebSocketOptions
|
||||
{
|
||||
Uri = uri,
|
||||
RequestHeaders = parameters?.RequestHeaders,
|
||||
CookieContainer = parameters?.CookieContainer
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(Action<MqttClientWebSocketOptions> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
|
||||
|
||||
_webSocketOptions = new MqttClientWebSocketOptions();
|
||||
optionsBuilder.Invoke(_webSocketOptions);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters)
|
||||
{
|
||||
_tlsParameters = parameters;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls()
|
||||
{
|
||||
return WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = true });
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls(Action<MqttClientOptionsBuilderTlsParameters> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null) throw new ArgumentNullException(nameof(optionsBuilder));
|
||||
|
||||
_tlsParameters = new MqttClientOptionsBuilderTlsParameters
|
||||
{
|
||||
UseTls = true
|
||||
};
|
||||
|
||||
optionsBuilder(_tlsParameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithPacketInspector(IMqttPacketInspector packetInspector)
|
||||
{
|
||||
_options.PacketInspector = packetInspector;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IMqttClientOptions Build()
|
||||
public MqttClientOptions Build()
|
||||
{
|
||||
if (_tcpOptions == null && _webSocketOptions == null)
|
||||
{
|
||||
@@ -293,20 +37,18 @@ namespace MQTTnet.Client.Options
|
||||
UseTls = true,
|
||||
SslProtocol = _tlsParameters.SslProtocol,
|
||||
AllowUntrustedCertificates = _tlsParameters.AllowUntrustedCertificates,
|
||||
CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
|
||||
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
|
||||
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors,
|
||||
#if WINDOWS_UWP
|
||||
Certificates = _tlsParameters.Certificates?.Select(c => c.ToArray()).ToList(),
|
||||
#else
|
||||
Certificates = _tlsParameters.Certificates?.ToList(),
|
||||
#endif
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
CertificateValidationCallback = _tlsParameters.CertificateValidationCallback,
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
|
||||
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
|
||||
ApplicationProtocols = _tlsParameters.ApplicationProtocols,
|
||||
#endif
|
||||
CertificateValidationHandler = _tlsParameters.CertificateValidationHandler,
|
||||
IgnoreCertificateChainErrors = _tlsParameters.IgnoreCertificateChainErrors,
|
||||
IgnoreCertificateRevocationErrors = _tlsParameters.IgnoreCertificateRevocationErrors
|
||||
};
|
||||
|
||||
if (_tcpOptions != null)
|
||||
@@ -334,5 +76,320 @@ namespace MQTTnet.Client.Options
|
||||
|
||||
return _options;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithAuthentication(string method, byte[] data)
|
||||
{
|
||||
_options.AuthenticationMethod = method;
|
||||
_options.AuthenticationData = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCleanSession(bool value = true)
|
||||
{
|
||||
_options.CleanSession = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithClientId(string value)
|
||||
{
|
||||
_options.ClientId = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the timeout which will be applied at socket level and internal operations.
|
||||
/// The default value is the same as for sockets in .NET in general.
|
||||
/// </summary>
|
||||
public MqttClientOptionsBuilder WithTimeout(TimeSpan value)
|
||||
{
|
||||
_options.Timeout = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithConnectionUri(Uri uri)
|
||||
{
|
||||
if (uri == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(uri));
|
||||
}
|
||||
|
||||
var port = uri.IsDefaultPort ? null : (int?)uri.Port;
|
||||
switch (uri.Scheme.ToLower())
|
||||
{
|
||||
case "tcp":
|
||||
case "mqtt":
|
||||
WithTcpServer(uri.Host, port);
|
||||
break;
|
||||
|
||||
case "mqtts":
|
||||
WithTcpServer(uri.Host, port).WithTls();
|
||||
break;
|
||||
|
||||
case "ws":
|
||||
case "wss":
|
||||
WithWebSocketServer(uri.ToString());
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Unexpected scheme in uri.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(uri.UserInfo))
|
||||
{
|
||||
var userInfo = uri.UserInfo.Split(':');
|
||||
var username = userInfo[0];
|
||||
var password = userInfo.Length > 1 ? userInfo[1] : "";
|
||||
WithCredentials(username, password);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithConnectionUri(string uri)
|
||||
{
|
||||
return WithConnectionUri(new Uri(uri, UriKind.Absolute));
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCredentials(string username, string password)
|
||||
{
|
||||
byte[] passwordBuffer = null;
|
||||
|
||||
if (password != null)
|
||||
{
|
||||
passwordBuffer = Encoding.UTF8.GetBytes(password);
|
||||
}
|
||||
|
||||
return WithCredentials(username, passwordBuffer);
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCredentials(string username, byte[] password = null)
|
||||
{
|
||||
return WithCredentials(new MqttClientCredentials(username, password));
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithCredentials(IMqttClientCredentialsProvider credentials)
|
||||
{
|
||||
_options.Credentials = credentials;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithExtendedAuthenticationExchangeHandler(IMqttExtendedAuthenticationExchangeHandler handler)
|
||||
{
|
||||
_options.ExtendedAuthenticationExchangeHandler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithKeepAlivePeriod(TimeSpan value)
|
||||
{
|
||||
_options.KeepAlivePeriod = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithMaximumPacketSize(uint maximumPacketSize)
|
||||
{
|
||||
_options.MaximumPacketSize = maximumPacketSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithNoKeepAlive()
|
||||
{
|
||||
return WithKeepAlivePeriod(TimeSpan.Zero);
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
|
||||
{
|
||||
if (value == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
throw new ArgumentException("Protocol version is invalid.");
|
||||
}
|
||||
|
||||
_options.ProtocolVersion = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithProxy(
|
||||
string address,
|
||||
string username = null,
|
||||
string password = null,
|
||||
string domain = null,
|
||||
bool bypassOnLocal = false,
|
||||
string[] bypassList = null)
|
||||
{
|
||||
_proxyOptions = new MqttClientWebSocketProxyOptions
|
||||
{
|
||||
Address = address,
|
||||
Username = username,
|
||||
Password = password,
|
||||
Domain = domain,
|
||||
BypassOnLocal = bypassOnLocal,
|
||||
BypassList = bypassList
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithProxy(Action<MqttClientWebSocketProxyOptions> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(optionsBuilder));
|
||||
}
|
||||
|
||||
_proxyOptions = new MqttClientWebSocketProxyOptions();
|
||||
optionsBuilder(_proxyOptions);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithReceiveMaximum(ushort receiveMaximum)
|
||||
{
|
||||
_options.ReceiveMaximum = receiveMaximum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithRequestProblemInformation(bool requestProblemInformation = true)
|
||||
{
|
||||
_options.RequestProblemInformation = requestProblemInformation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithRequestResponseInformation(bool requestResponseInformation = true)
|
||||
{
|
||||
_options.RequestResponseInformation = requestResponseInformation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithSessionExpiryInterval(uint sessionExpiryInterval)
|
||||
{
|
||||
_options.SessionExpiryInterval = sessionExpiryInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTcpServer(string server, int? port = null)
|
||||
{
|
||||
_tcpOptions = new MqttClientTcpOptions
|
||||
{
|
||||
Server = server,
|
||||
Port = port
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO: Consider creating _MqttClientTcpOptionsBuilder_ as overload.
|
||||
public MqttClientOptionsBuilder WithTcpServer(Action<MqttClientTcpOptions> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(optionsBuilder));
|
||||
}
|
||||
|
||||
_tcpOptions = new MqttClientTcpOptions();
|
||||
optionsBuilder.Invoke(_tcpOptions);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls(MqttClientOptionsBuilderTlsParameters parameters)
|
||||
{
|
||||
_tlsParameters = parameters;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls()
|
||||
{
|
||||
return WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = true });
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTls(Action<MqttClientOptionsBuilderTlsParameters> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(optionsBuilder));
|
||||
}
|
||||
|
||||
_tlsParameters = new MqttClientOptionsBuilderTlsParameters
|
||||
{
|
||||
UseTls = true
|
||||
};
|
||||
|
||||
optionsBuilder(_tlsParameters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithTopicAliasMaximum(ushort topicAliasMaximum)
|
||||
{
|
||||
_options.TopicAliasMaximum = topicAliasMaximum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithUserProperty(string name, string value)
|
||||
{
|
||||
if (name is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (_options.UserProperties == null)
|
||||
{
|
||||
_options.UserProperties = new List<MqttUserProperty>();
|
||||
}
|
||||
|
||||
_options.UserProperties.Add(new MqttUserProperty(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(string uri, MqttClientOptionsBuilderWebSocketParameters parameters = null)
|
||||
{
|
||||
_webSocketOptions = new MqttClientWebSocketOptions
|
||||
{
|
||||
Uri = uri,
|
||||
RequestHeaders = parameters?.RequestHeaders,
|
||||
CookieContainer = parameters?.CookieContainer
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWebSocketServer(Action<MqttClientWebSocketOptions> optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(optionsBuilder));
|
||||
}
|
||||
|
||||
_webSocketOptions = new MqttClientWebSocketOptions();
|
||||
optionsBuilder.Invoke(_webSocketOptions);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillDelayInterval(uint willDelayInterval)
|
||||
{
|
||||
_options.WillDelayInterval = willDelayInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillPayload(byte[] willPayload)
|
||||
{
|
||||
_options.WillPayload = willPayload;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel willQualityOfServiceLevel)
|
||||
{
|
||||
_options.WillQualityOfServiceLevel = willQualityOfServiceLevel;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder WithWillTopic(string willTopic)
|
||||
{
|
||||
_options.WillTopic = willTopic;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientOptionsBuilderTlsParameters
|
||||
{
|
||||
public bool UseTls { get; set; }
|
||||
|
||||
[Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
|
||||
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Func<MqttClientCertificateValidationCallbackContext, bool> CertificateValidationHandler { get; set; }
|
||||
|
||||
public Func<MqttClientCertificateValidationEventArgs, bool> CertificateValidationHandler { get; set; }
|
||||
|
||||
#if NET48 || NETCOREAPP3_1 || NET5 || NET6
|
||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12 | SslProtocols.Tls13;
|
||||
@@ -31,8 +28,8 @@ namespace MQTTnet.Client.Options
|
||||
public IEnumerable<X509Certificate> Certificates { get; set; }
|
||||
#endif
|
||||
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
public List<SslApplicationProtocol> ApplicationProtocols { get;set; }
|
||||
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
|
||||
public List<SslApplicationProtocol> ApplicationProtocols { get;set; }
|
||||
#endif
|
||||
|
||||
public bool AllowUntrustedCertificates { get; set; }
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientOptionsBuilderWebSocketParameters
|
||||
{
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
using System.Net.Sockets;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientTcpOptions : IMqttClientChannelOptions
|
||||
public sealed class MqttClientTcpOptions : IMqttClientChannelOptions
|
||||
{
|
||||
public string Server { get; set; }
|
||||
|
||||
public int? Port { get; set; }
|
||||
public AddressFamily AddressFamily { get; set; } = AddressFamily.Unspecified;
|
||||
|
||||
public int BufferSize { get; set; } = 8192;
|
||||
|
||||
public bool? DualMode { get; set; }
|
||||
|
||||
public LingerOption LingerState { get; set; } = new LingerOption(true, 0);
|
||||
|
||||
public bool NoDelay { get; set; } = true;
|
||||
|
||||
public AddressFamily AddressFamily { get; set; } = AddressFamily.Unspecified;
|
||||
public int? Port { get; set; }
|
||||
|
||||
public string Server { get; set; }
|
||||
|
||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
||||
|
||||
@@ -23,4 +29,4 @@ namespace MQTTnet.Client.Options
|
||||
return Server + ":" + this.GetPort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public static class MqttClientTcpOptionsExtensions
|
||||
{
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientTlsOptions
|
||||
public sealed class MqttClientTlsOptions
|
||||
{
|
||||
public Func<MqttClientCertificateValidationEventArgs, bool> CertificateValidationHandler { get; set; }
|
||||
|
||||
public bool UseTls { get; set; }
|
||||
|
||||
public bool IgnoreCertificateRevocationErrors { get; set; }
|
||||
@@ -22,8 +27,8 @@ namespace MQTTnet.Client.Options
|
||||
public List<X509Certificate> Certificates { get; set; }
|
||||
#endif
|
||||
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
public List<SslApplicationProtocol> ApplicationProtocols { get; set; }
|
||||
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
|
||||
public List<System.Net.Security.SslApplicationProtocol> ApplicationProtocols { get; set; }
|
||||
#endif
|
||||
|
||||
#if NET48 || NETCOREAPP3_1 || NET5 || NET6
|
||||
@@ -31,10 +36,5 @@ namespace MQTTnet.Client.Options
|
||||
#else
|
||||
public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12 | (SslProtocols)0x00003000 /*Tls13*/;
|
||||
#endif
|
||||
|
||||
[Obsolete("This property will be removed soon. Use CertificateValidationHandler instead.")]
|
||||
public Func<X509Certificate, X509Chain, SslPolicyErrors, IMqttClientOptions, bool> CertificateValidationCallback { get; set; }
|
||||
|
||||
public Func<MqttClientCertificateValidationCallbackContext, bool> CertificateValidationHandler { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace MQTTnet.Client.Options
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientWebSocketOptions : IMqttClientChannelOptions
|
||||
{
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Client.Options
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientWebSocketProxyOptions
|
||||
{
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
namespace MQTTnet.Client.Publishing
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public enum MqttClientPublishReasonCode
|
||||
{
|
||||
Success = 0,
|
||||
|
||||
NoMatchingSubscribers = 16,
|
||||
UnspecifiedError = 128,
|
||||
ImplementationSpecificError = 131,
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client.Publishing
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientPublishResult
|
||||
public sealed class MqttClientPublishResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the packet identifier which was used for this publish.
|
||||
/// </summary>
|
||||
public ushort? PacketIdentifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -27,6 +33,6 @@ namespace MQTTnet.Client.Publishing
|
||||
/// The feature is very similar to the HTTP header concept.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public List<MqttUserProperty> UserProperties { get; set; }
|
||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; internal set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientPublishResultFactory
|
||||
{
|
||||
static readonly MqttClientPublishResult EmptySuccessResult = new MqttClientPublishResult();
|
||||
static readonly IReadOnlyCollection<MqttUserProperty> EmptyUserProperties = new List<MqttUserProperty>();
|
||||
|
||||
public MqttClientPublishResult Create(MqttPubAckPacket pubAckPacket)
|
||||
{
|
||||
// QoS 0 has no response. So we treat it as a success always.
|
||||
if (pubAckPacket == null)
|
||||
{
|
||||
return EmptySuccessResult;
|
||||
}
|
||||
|
||||
var result = new MqttClientPublishResult
|
||||
{
|
||||
// Both enums have the same values. So it can be easily converted.
|
||||
ReasonCode = (MqttClientPublishReasonCode)(int)pubAckPacket.ReasonCode,
|
||||
PacketIdentifier = pubAckPacket.PacketIdentifier,
|
||||
ReasonString = pubAckPacket.ReasonString,
|
||||
UserProperties = pubAckPacket.UserProperties ?? EmptyUserProperties
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public MqttClientPublishResult Create(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket)
|
||||
{
|
||||
if (pubRecPacket == null || pubCompPacket == null)
|
||||
{
|
||||
return new MqttClientPublishResult
|
||||
{
|
||||
ReasonCode = MqttClientPublishReasonCode.UnspecifiedError
|
||||
};
|
||||
}
|
||||
|
||||
MqttClientPublishResult result;
|
||||
|
||||
// The PUBCOMP is the last packet in QoS 2. So we use the results from that instead of PUBREC.
|
||||
if (pubCompPacket.ReasonCode == MqttPubCompReasonCode.PacketIdentifierNotFound)
|
||||
{
|
||||
result = new MqttClientPublishResult
|
||||
{
|
||||
PacketIdentifier = pubCompPacket.PacketIdentifier,
|
||||
ReasonCode = MqttClientPublishReasonCode.UnspecifiedError,
|
||||
ReasonString = pubCompPacket.ReasonString,
|
||||
UserProperties = pubCompPacket.UserProperties ?? EmptyUserProperties
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
result = new MqttClientPublishResult
|
||||
{
|
||||
PacketIdentifier = pubCompPacket.PacketIdentifier,
|
||||
ReasonCode = MqttClientPublishReasonCode.Success,
|
||||
ReasonString = pubCompPacket.ReasonString,
|
||||
UserProperties = pubCompPacket.UserProperties ?? EmptyUserProperties
|
||||
};
|
||||
|
||||
if (pubRecPacket.ReasonCode != MqttPubRecReasonCode.Success)
|
||||
{
|
||||
// Both enums share the same values.
|
||||
result.ReasonCode = (MqttClientPublishReasonCode)pubRecPacket.ReasonCode;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Client.Receiving
|
||||
{
|
||||
public interface IMqttApplicationMessageReceivedHandler
|
||||
{
|
||||
Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
+20
-2
@@ -1,9 +1,14 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
@@ -23,7 +28,7 @@ namespace MQTTnet
|
||||
_acknowledgeHandler = acknowledgeHandler;
|
||||
}
|
||||
|
||||
internal MqttPublishPacket PublishPacket { get; }
|
||||
internal MqttPublishPacket PublishPacket { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client identifier.
|
||||
@@ -35,7 +40,20 @@ namespace MQTTnet
|
||||
|
||||
public bool ProcessingFailed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason code which will be sent to the server.
|
||||
/// </summary>
|
||||
public MqttApplicationMessageReceivedReasonCode ReasonCode { get; set; } = MqttApplicationMessageReceivedReasonCode.Success;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user properties which will be sent to the server in the ACK packet etc.
|
||||
/// </summary>
|
||||
public List<MqttUserProperty> ResponseUserProperties { get; } = new List<MqttUserProperty>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reason string which will be sent to the server in the ACK packet.
|
||||
/// </summary>
|
||||
public string ResponseReasonString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this message was handled.
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Client.Receiving
|
||||
{
|
||||
public sealed class MqttApplicationMessageReceivedHandlerDelegate : IMqttApplicationMessageReceivedHandler
|
||||
{
|
||||
readonly Func<MqttApplicationMessageReceivedEventArgs, Task> _handler;
|
||||
|
||||
public MqttApplicationMessageReceivedHandlerDelegate(Action<MqttApplicationMessageReceivedEventArgs> handler)
|
||||
{
|
||||
if (handler == null) throw new ArgumentNullException(nameof(handler));
|
||||
|
||||
_handler = context =>
|
||||
{
|
||||
handler(context);
|
||||
return PlatformAbstractionLayer.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
public MqttApplicationMessageReceivedHandlerDelegate(Func<MqttApplicationMessageReceivedEventArgs, Task> handler)
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs context)
|
||||
{
|
||||
return _handler(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public enum MqttApplicationMessageReceivedReasonCode
|
||||
{
|
||||
@@ -1,9 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client.Subscribing
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientSubscribeOptions
|
||||
public sealed class MqttClientSubscribeOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a list of topic filters the client wants to subscribe to.
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Client.Subscribing
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientSubscribeOptionsBuilder
|
||||
{
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client.Subscribing
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientSubscribeResult
|
||||
public sealed class MqttClientSubscribeResult
|
||||
{
|
||||
public List<MqttClientSubscribeResultItem> Items { get; } = new List<MqttClientSubscribeResultItem>();
|
||||
public IReadOnlyCollection<MqttClientSubscribeResultItem> Items { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user properties which were part of the SUBACK packet.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason string.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public string ReasonString { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the packet identifier which was used.
|
||||
/// </summary>
|
||||
public ushort PacketIdentifier { get; internal set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
namespace MQTTnet.Client.Subscribing
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public enum MqttClientSubscribeResultCode
|
||||
{
|
||||
GrantedQoS0 = 0,
|
||||
GrantedQoS1 = 1,
|
||||
GrantedQoS2 = 2,
|
||||
UnspecifiedError = 128,
|
||||
GrantedQoS0 = 0x00,
|
||||
GrantedQoS1 = 0x01,
|
||||
GrantedQoS2 = 0x02,
|
||||
UnspecifiedError = 0x80,
|
||||
|
||||
ImplementationSpecificError = 131,
|
||||
NotAuthorized = 135,
|
||||
TopicFilterInvalid = 143,
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientSubscribeResultFactory
|
||||
{
|
||||
static readonly IReadOnlyCollection<MqttUserProperty> EmptyUserProperties = new List<MqttUserProperty>();
|
||||
|
||||
public MqttClientSubscribeResult Create(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
|
||||
{
|
||||
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
||||
if (subAckPacket == null) throw new ArgumentNullException(nameof(subAckPacket));
|
||||
|
||||
// MQTTv5.0.0 handling.
|
||||
if (subAckPacket.ReasonCodes.Any() && subAckPacket.ReasonCodes.Count != subscribePacket.TopicFilters.Count)
|
||||
{
|
||||
throw new MqttProtocolViolationException(
|
||||
"The reason codes are not matching the topic filters [MQTT-3.9.3-1].");
|
||||
}
|
||||
|
||||
var items = new List<MqttClientSubscribeResultItem>();
|
||||
for (var i = 0; i < subscribePacket.TopicFilters.Count; i++)
|
||||
{
|
||||
items.Add(CreateSubscribeResultItem(i, subscribePacket, subAckPacket));
|
||||
}
|
||||
|
||||
var result = new MqttClientSubscribeResult
|
||||
{
|
||||
PacketIdentifier = subAckPacket.PacketIdentifier,
|
||||
ReasonString = subAckPacket.ReasonString,
|
||||
UserProperties = subAckPacket.UserProperties ?? EmptyUserProperties,
|
||||
Items = items
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static MqttClientSubscribeResultItem CreateSubscribeResultItem(int index, MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
|
||||
{
|
||||
var resultCode = (MqttClientSubscribeResultCode) subAckPacket.ReasonCodes[index];
|
||||
|
||||
return new MqttClientSubscribeResultItem
|
||||
{
|
||||
TopicFilter = subscribePacket.TopicFilters[index],
|
||||
ResultCode = resultCode,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,23 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client.Subscribing
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientSubscribeResultItem
|
||||
public sealed class MqttClientSubscribeResultItem
|
||||
{
|
||||
public MqttClientSubscribeResultItem(MqttTopicFilter topicFilter, MqttClientSubscribeResultCode resultCode)
|
||||
{
|
||||
TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter));
|
||||
ResultCode = resultCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the topic filter.
|
||||
/// The topic filter can contain topics and wildcards.
|
||||
/// </summary>
|
||||
public MqttTopicFilter TopicFilter { get; }
|
||||
public MqttTopicFilter TopicFilter { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the result code.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientSubscribeResultCode ResultCode { get; }
|
||||
public MqttClientSubscribeResultCode ResultCode { get; internal set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client.Unsubscribing
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientUnsubscribeOptions
|
||||
public sealed class MqttClientUnsubscribeOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a list of topic filters the client wants to unsubscribe from.
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using MQTTnet.Packets;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client.Unsubscribing
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientUnsubscribeOptionsBuilder
|
||||
{
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client.Unsubscribing
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientUnsubscribeResult
|
||||
public sealed class MqttClientUnsubscribeResult
|
||||
{
|
||||
public List<MqttClientUnsubscribeResultItem> Items { get; } =new List<MqttClientUnsubscribeResultItem>();
|
||||
public IReadOnlyCollection<MqttClientUnsubscribeResultItem> Items { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user properties which were part of the UNSUBACK packet.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<MqttUserProperty> UserProperties { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason string.
|
||||
/// MQTTv5 only.
|
||||
/// </summary>
|
||||
public string ReasonString { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the packet identifier which was used.
|
||||
/// </summary>
|
||||
public ushort PacketIdentifier { get; internal set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Client.Unsubscribing
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public enum MqttClientUnsubscribeResultCode
|
||||
{
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public sealed class MqttClientUnsubscribeResultFactory
|
||||
{
|
||||
static readonly IReadOnlyCollection<MqttUserProperty> EmptyUserProperties = new List<MqttUserProperty>();
|
||||
|
||||
public MqttClientUnsubscribeResult Create(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket)
|
||||
{
|
||||
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
|
||||
if (unsubAckPacket == null) throw new ArgumentNullException(nameof(unsubAckPacket));
|
||||
|
||||
// MQTTv3.1.1 has no reason code at all!
|
||||
if (unsubAckPacket.ReasonCodes != null && unsubAckPacket.ReasonCodes.Count != unsubscribePacket.TopicFilters.Count)
|
||||
{
|
||||
throw new MqttProtocolViolationException(
|
||||
"The return codes are not matching the topic filters [MQTT-3.9.3-1].");
|
||||
}
|
||||
|
||||
var items = new List<MqttClientUnsubscribeResultItem>();
|
||||
for (var i = 0; i < unsubscribePacket.TopicFilters.Count; i++)
|
||||
{
|
||||
items.Add(CreateUnsubscribeResultItem(i, unsubscribePacket, unsubAckPacket));
|
||||
}
|
||||
|
||||
var result = new MqttClientUnsubscribeResult
|
||||
{
|
||||
PacketIdentifier = unsubAckPacket.PacketIdentifier,
|
||||
ReasonString = unsubAckPacket.ReasonString,
|
||||
UserProperties = unsubAckPacket.UserProperties ?? EmptyUserProperties,
|
||||
Items = items
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static MqttClientUnsubscribeResultItem CreateUnsubscribeResultItem(int index, MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket)
|
||||
{
|
||||
var resultCode = MqttClientUnsubscribeResultCode.Success;
|
||||
|
||||
if (unsubAckPacket.ReasonCodes != null)
|
||||
{
|
||||
// MQTTv3.1.1 has no reason code and no return code!.
|
||||
resultCode = (MqttClientUnsubscribeResultCode) unsubAckPacket.ReasonCodes[index];
|
||||
}
|
||||
|
||||
return new MqttClientUnsubscribeResultItem
|
||||
{
|
||||
TopicFilter = unsubscribePacket.TopicFilters[index],
|
||||
ResultCode = resultCode
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,21 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Client.Unsubscribing
|
||||
namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttClientUnsubscribeResultItem
|
||||
public sealed class MqttClientUnsubscribeResultItem
|
||||
{
|
||||
public MqttClientUnsubscribeResultItem(string topicFilter, MqttClientUnsubscribeResultCode reasonCode)
|
||||
{
|
||||
TopicFilter = topicFilter ?? throw new ArgumentNullException(nameof(topicFilter));
|
||||
ReasonCode = reasonCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the topic filter.
|
||||
/// The topic filter can contain topics and wildcards.
|
||||
/// </summary>
|
||||
public string TopicFilter { get; }
|
||||
|
||||
public string TopicFilter { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the result code.
|
||||
/// Hint: MQTT 5 feature only.
|
||||
/// </summary>
|
||||
public MqttClientUnsubscribeResultCode ReasonCode { get; }
|
||||
public MqttClientUnsubscribeResultCode ResultCode { get; internal set; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Diagnostics.Logger
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Diagnostics
|
||||
{
|
||||
public interface IMqttNetLogger
|
||||
{
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Diagnostics.Logger
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// This logger fires an event when a new message was published.
|
||||
/// This logger fires an event when a new message was published.
|
||||
/// </summary>
|
||||
public sealed class MqttNetEventLogger : IMqttNetLogger
|
||||
{
|
||||
@@ -14,9 +18,9 @@ namespace MQTTnet.Diagnostics.Logger
|
||||
|
||||
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
||||
|
||||
public string LogId { get; }
|
||||
public bool IsEnabled => LogMessagePublished != null;
|
||||
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
public string LogId { get; }
|
||||
|
||||
public void Publish(MqttNetLogLevel level, string source, string message, object[] parameters, Exception exception)
|
||||
{
|
||||
@@ -28,7 +32,7 @@ namespace MQTTnet.Diagnostics.Logger
|
||||
// might be null after preparing the message.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (parameters?.Length > 0 && message?.Length > 0)
|
||||
{
|
||||
try
|
||||
@@ -53,7 +57,7 @@ namespace MQTTnet.Diagnostics.Logger
|
||||
Message = message,
|
||||
Exception = exception
|
||||
};
|
||||
|
||||
|
||||
eventHandler.Invoke(this, new MqttNetLogMessagePublishedEventArgs(logMessage));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Diagnostics.Logger
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Diagnostics
|
||||
{
|
||||
public enum MqttNetLogLevel
|
||||
{
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Diagnostics.Logger
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Diagnostics
|
||||
{
|
||||
public sealed class MqttNetLogMessage
|
||||
{
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Diagnostics.Logger
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Diagnostics
|
||||
{
|
||||
public sealed class MqttNetLogMessagePublishedEventArgs : EventArgs
|
||||
{
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Diagnostics.Logger
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
/// This logger does nothing with the messages.
|
||||
/// </summary>
|
||||
public sealed class MqttNetNullLogger : IMqttNetLogger
|
||||
{
|
||||
public static MqttNetNullLogger Instance { get; } = new MqttNetNullLogger();
|
||||
|
||||
public bool IsEnabled { get; }
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Diagnostics.Logger
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Diagnostics
|
||||
{
|
||||
public sealed class MqttNetSourceLogger
|
||||
{
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Diagnostics.Logger
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Diagnostics
|
||||
{
|
||||
public static class MqttNetSourceLoggerExtensions
|
||||
{
|
||||
@@ -126,6 +130,16 @@ namespace MQTTnet.Diagnostics.Logger
|
||||
logger.Publish(MqttNetLogLevel.Warning, message, new object[] {parameter1}, null);
|
||||
}
|
||||
|
||||
public static void Warning<TParameter1, TParameter2>(this MqttNetSourceLogger logger, string message, TParameter1 parameter1, TParameter2 parameter2)
|
||||
{
|
||||
if (!logger.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.Publish(MqttNetLogLevel.Warning, message, new object[] {parameter1, parameter2}, null);
|
||||
}
|
||||
|
||||
public static void Warning(this MqttNetSourceLogger logger, string message)
|
||||
{
|
||||
if (!logger.IsEnabled)
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace MQTTnet.Diagnostics.PacketInspection
|
||||
{
|
||||
public interface IMqttPacketInspector
|
||||
{
|
||||
void ProcessMqttPacket(ProcessMqttPacketContext context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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.Diagnostics
|
||||
{
|
||||
public sealed class InspectMqttPacketEventArgs : EventArgs
|
||||
{
|
||||
public MqttPacketFlowDirection Direction { get; internal set; }
|
||||
|
||||
public byte[] Buffer { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Diagnostics.PacketInspection
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Diagnostics
|
||||
{
|
||||
public enum MqttPacketFlowDirection
|
||||
{
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace MQTTnet.Diagnostics.PacketInspection
|
||||
{
|
||||
public sealed class ProcessMqttPacketContext
|
||||
{
|
||||
public MqttPacketFlowDirection Direction { get; set; }
|
||||
|
||||
public byte[] Buffer { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace MQTTnet.Diagnostics.Runtime
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Diagnostics
|
||||
{
|
||||
public static class TargetFrameworkProvider
|
||||
{
|
||||
@@ -24,6 +28,8 @@
|
||||
return "netcoreapp3.1";
|
||||
#elif NET5_0
|
||||
return "net5.0";
|
||||
#elif NET6_0
|
||||
return "net6.0";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// 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.Exceptions
|
||||
{
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using System;
|
||||
// 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.Exceptions
|
||||
{
|
||||
public class MqttCommunicationTimedOutException : MqttCommunicationException
|
||||
public sealed class MqttCommunicationTimedOutException : MqttCommunicationException
|
||||
{
|
||||
public MqttCommunicationTimedOutException()
|
||||
public MqttCommunicationTimedOutException() : base("The operation has timed out.")
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// 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.Exceptions
|
||||
{
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
// 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.Exceptions
|
||||
{
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
@@ -10,10 +14,10 @@ namespace MQTTnet.Exceptions
|
||||
: base($"Unexpected DISCONNECT (Reason code={disconnectPacket.ReasonCode}) received.")
|
||||
{
|
||||
ReasonCode = disconnectPacket.ReasonCode;
|
||||
SessionExpiryInterval = disconnectPacket.Properties?.SessionExpiryInterval;
|
||||
ReasonString = disconnectPacket.Properties?.ReasonString;
|
||||
ServerReference = disconnectPacket.Properties?.ServerReference;
|
||||
UserProperties = disconnectPacket.Properties?.UserProperties;
|
||||
SessionExpiryInterval = disconnectPacket.SessionExpiryInterval;
|
||||
ReasonString = disconnectPacket.ReasonString;
|
||||
ServerReference = disconnectPacket.ServerReference;
|
||||
UserProperties = disconnectPacket.UserProperties;
|
||||
}
|
||||
|
||||
public MqttDisconnectReasonCode? ReasonCode { get; }
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MQTTnet.Client.Options;
|
||||
|
||||
namespace MQTTnet.Extensions
|
||||
{
|
||||
public static class MqttClientOptionsBuilderExtension
|
||||
{
|
||||
public static MqttClientOptionsBuilder WithConnectionUri(this MqttClientOptionsBuilder builder, Uri uri)
|
||||
{
|
||||
var port = uri.IsDefaultPort ? null : (int?) uri.Port;
|
||||
switch (uri.Scheme.ToLower())
|
||||
{
|
||||
case "tcp":
|
||||
case "mqtt":
|
||||
builder.WithTcpServer(uri.Host, port);
|
||||
break;
|
||||
|
||||
case "mqtts":
|
||||
builder.WithTcpServer(uri.Host, port).WithTls();
|
||||
break;
|
||||
|
||||
case "ws":
|
||||
case "wss":
|
||||
builder.WithWebSocketServer(uri.ToString());
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Unexpected scheme in uri.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(uri.UserInfo))
|
||||
{
|
||||
var userInfo = uri.UserInfo.Split(':');
|
||||
var username = userInfo[0];
|
||||
var password = userInfo.Length > 1 ? userInfo[1] : "";
|
||||
builder.WithCredentials(username, password);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static MqttClientOptionsBuilder WithConnectionUri(this MqttClientOptionsBuilder builder, string uri)
|
||||
{
|
||||
return WithConnectionUri(builder, new Uri(uri, UriKind.Absolute));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace MQTTnet.Extensions
|
||||
{
|
||||
public static class UserPropertyExtension
|
||||
{
|
||||
public static string GetUserProperty(this MqttApplicationMessage message, string propertyName, StringComparison comparisonType = StringComparison.Ordinal)
|
||||
{
|
||||
if (message == null) throw new ArgumentNullException(nameof(message));
|
||||
if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));
|
||||
|
||||
return message.UserProperties?.SingleOrDefault(up => up.Name.Equals(propertyName, comparisonType))?.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Client.Connecting;
|
||||
using MQTTnet.Client.Disconnecting;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Server.Internal;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public interface IMqttDataConverter
|
||||
{
|
||||
MqttApplicationMessage CreateApplicationMessage(MqttPublishPacket publishPacket);
|
||||
|
||||
MqttClientConnectResult CreateClientConnectResult(MqttConnAckPacket connAckPacket);
|
||||
|
||||
MqttClientPublishResult CreateClientPublishResult(MqttPubAckPacket pubAckPacket);
|
||||
|
||||
MqttClientPublishResult CreateClientPublishResult(MqttPubRecPacket pubRecPacket, MqttPubCompPacket pubCompPacket);
|
||||
|
||||
MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket);
|
||||
|
||||
MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket);
|
||||
|
||||
MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options);
|
||||
|
||||
MqttConnAckPacket CreateConnAckPacket(MqttConnectionValidatorContext connectionValidatorContext);
|
||||
|
||||
MqttPublishPacket CreatePublishPacket(MqttApplicationMessage applicationMessage);
|
||||
|
||||
MqttPubAckPacket CreatePubAckPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode);
|
||||
|
||||
MqttPubRecPacket CreatePubRecPacket(MqttPublishPacket publishPacket, MqttApplicationMessageReceivedReasonCode reasonCode);
|
||||
|
||||
MqttPubCompPacket CreatePubCompPacket(MqttPubRelPacket pubRelPacket, MqttApplicationMessageReceivedReasonCode reasonCode);
|
||||
|
||||
MqttPubRelPacket CreatePubRelPacket(MqttPubRecPacket pubRecPacket, MqttApplicationMessageReceivedReasonCode reasonCode);
|
||||
|
||||
MqttSubscribePacket CreateSubscribePacket(MqttClientSubscribeOptions options);
|
||||
|
||||
MqttSubAckPacket CreateSubAckPacket(MqttSubscribePacket subscribePacket, SubscribeResult subscribeResult);
|
||||
|
||||
MqttUnsubscribePacket CreateUnsubscribePacket(MqttClientUnsubscribeOptions options);
|
||||
|
||||
MqttUnsubAckPacket CreateUnsubAckPacket(MqttUnsubscribePacket unsubscribePacket, List<MqttUnsubscribeReasonCode> reasonCodes);
|
||||
|
||||
MqttDisconnectPacket CreateDisconnectPacket(MqttClientDisconnectOptions options);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public interface IMqttPacketBodyReader
|
||||
{
|
||||
int Length { get; }
|
||||
|
||||
int Offset { get; }
|
||||
|
||||
bool EndOfStream { get; }
|
||||
|
||||
byte ReadByte();
|
||||
|
||||
byte[] ReadRemainingData();
|
||||
|
||||
ushort ReadTwoByteInteger();
|
||||
|
||||
string ReadStringWithLengthPrefix();
|
||||
|
||||
byte[] ReadWithLengthPrefix();
|
||||
|
||||
uint ReadFourByteInteger();
|
||||
|
||||
uint ReadVariableLengthInteger();
|
||||
|
||||
bool ReadBoolean();
|
||||
|
||||
void Seek(int position);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
@@ -6,12 +9,8 @@ namespace MQTTnet.Formatter
|
||||
{
|
||||
public interface IMqttPacketFormatter
|
||||
{
|
||||
IMqttDataConverter DataConverter { get; }
|
||||
|
||||
ArraySegment<byte> Encode(MqttBasePacket mqttPacket);
|
||||
|
||||
MqttBasePacket Decode(ReceivedMqttPacket receivedMqttPacket);
|
||||
|
||||
void FreeBuffer();
|
||||
MqttPacket Decode(ReceivedMqttPacket receivedMqttPacket);
|
||||
|
||||
MqttPacketBuffer Encode(MqttPacket mqttPacket);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public interface IMqttPacketWriter
|
||||
{
|
||||
int Length { get; }
|
||||
|
||||
void WriteWithLengthPrefix(string value);
|
||||
|
||||
void Write(byte value);
|
||||
|
||||
void WriteWithLengthPrefix(byte[] value);
|
||||
|
||||
void Write(ushort value);
|
||||
|
||||
void Write(IMqttPacketWriter value);
|
||||
|
||||
void WriteVariableLengthInteger(uint value);
|
||||
|
||||
void Write(byte[] value, int offset, int length);
|
||||
|
||||
void Reset(int length);
|
||||
|
||||
void Seek(int offset);
|
||||
|
||||
void FreeBuffer();
|
||||
|
||||
byte[] GetBuffer();
|
||||
}
|
||||
}
|
||||
@@ -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 MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public sealed class MqttApplicationMessageFactory
|
||||
{
|
||||
public MqttApplicationMessage Create(MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (publishPacket == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(publishPacket));
|
||||
}
|
||||
|
||||
return new MqttApplicationMessage
|
||||
{
|
||||
Topic = publishPacket.Topic,
|
||||
Payload = publishPacket.Payload,
|
||||
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
|
||||
Retain = publishPacket.Retain,
|
||||
Dup = publishPacket.Dup,
|
||||
ResponseTopic = publishPacket.ResponseTopic,
|
||||
ContentType = publishPacket.ContentType,
|
||||
CorrelationData = publishPacket.CorrelationData,
|
||||
MessageExpiryInterval = publishPacket.MessageExpiryInterval,
|
||||
SubscriptionIdentifiers = publishPacket.SubscriptionIdentifiers,
|
||||
TopicAlias = publishPacket.TopicAlias,
|
||||
PayloadFormatIndicator = publishPacket.PayloadFormatIndicator,
|
||||
UserProperties = publishPacket.UserProperties
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public sealed class MqttBufferReader
|
||||
{
|
||||
byte[] _buffer = PlatformAbstractionLayer.EmptyByteArray;
|
||||
int _initialPosition;
|
||||
int _length;
|
||||
int _position;
|
||||
|
||||
public bool EndOfStream => _position == _length;
|
||||
|
||||
public int Position => _position;
|
||||
|
||||
public int BytesLeft => _length - _position;
|
||||
|
||||
public byte[] ReadBinaryData()
|
||||
{
|
||||
var length = ReadTwoByteInteger();
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return PlatformAbstractionLayer.EmptyByteArray;
|
||||
}
|
||||
|
||||
ValidateReceiveBuffer(length);
|
||||
|
||||
var result = new byte[length];
|
||||
Array.Copy(_buffer, _position, result, 0, length);
|
||||
_position += length;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
ValidateReceiveBuffer(1);
|
||||
|
||||
return _buffer[_position++];
|
||||
}
|
||||
|
||||
public uint ReadFourByteInteger()
|
||||
{
|
||||
ValidateReceiveBuffer(4);
|
||||
|
||||
var byte0 = _buffer[_position++];
|
||||
var byte1 = _buffer[_position++];
|
||||
var byte2 = _buffer[_position++];
|
||||
var byte3 = _buffer[_position++];
|
||||
|
||||
return (uint)((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
|
||||
}
|
||||
|
||||
public byte[] ReadRemainingData()
|
||||
{
|
||||
var bufferLength = _length - _position;
|
||||
|
||||
if (bufferLength == 0)
|
||||
{
|
||||
return PlatformAbstractionLayer.EmptyByteArray;
|
||||
}
|
||||
|
||||
var buffer = new byte[bufferLength];
|
||||
Array.Copy(_buffer, _position, buffer, 0, bufferLength);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public string ReadString()
|
||||
{
|
||||
var length = ReadTwoByteInteger();
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
ValidateReceiveBuffer(length);
|
||||
|
||||
var result = Encoding.UTF8.GetString(_buffer, _position, length);
|
||||
_position += length;
|
||||
return result;
|
||||
}
|
||||
|
||||
public ushort ReadTwoByteInteger()
|
||||
{
|
||||
ValidateReceiveBuffer(2);
|
||||
|
||||
var msb = _buffer[_position++];
|
||||
var lsb = _buffer[_position++];
|
||||
|
||||
return (ushort)((msb << 8) | lsb);
|
||||
}
|
||||
|
||||
public uint ReadVariableByteInteger()
|
||||
{
|
||||
var multiplier = 1;
|
||||
var value = 0U;
|
||||
byte encodedByte;
|
||||
|
||||
do
|
||||
{
|
||||
encodedByte = ReadByte();
|
||||
value += (uint)((encodedByte & 127) * multiplier);
|
||||
|
||||
if (multiplier > 2097152)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Variable length integer is invalid.");
|
||||
}
|
||||
|
||||
multiplier *= 128;
|
||||
} while ((encodedByte & 128) != 0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Seek(int position)
|
||||
{
|
||||
_position = _initialPosition + position;
|
||||
}
|
||||
|
||||
public void SetBuffer(byte[] buffer, int position, int length)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_initialPosition = position;
|
||||
_position = position;
|
||||
_length = length;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void ValidateReceiveBuffer(int length)
|
||||
{
|
||||
if (_length < _position + length)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Expected at least {Position + length} bytes but there are only {_length} bytes");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
// 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.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a custom implementation of a memory stream which provides only MQTTnet relevant features.
|
||||
/// The goal is to avoid lots of argument checks like in the original stream. The growth rule is the
|
||||
/// same as for the original MemoryStream in .net. Also this implementation allows accessing the internal
|
||||
/// buffer for all platforms and .net framework versions (which is not available at the regular MemoryStream).
|
||||
/// </summary>
|
||||
public sealed class MqttBufferWriter
|
||||
{
|
||||
public const uint VariableByteIntegerMaxValue = 268435455;
|
||||
|
||||
readonly int _maxBufferSize;
|
||||
|
||||
byte[] _buffer;
|
||||
int _position;
|
||||
|
||||
public MqttBufferWriter(int bufferSize, int maxBufferSize)
|
||||
{
|
||||
_buffer = new byte[bufferSize];
|
||||
_maxBufferSize = maxBufferSize;
|
||||
}
|
||||
|
||||
public int Length { get; private set; }
|
||||
|
||||
public static byte BuildFixedHeader(MqttControlPacketType packetType, byte flags = 0)
|
||||
{
|
||||
var fixedHeader = (int)packetType << 4;
|
||||
fixedHeader |= flags;
|
||||
return (byte)fixedHeader;
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
// This method frees the used memory by shrinking the buffer. This is required because the buffer
|
||||
// is used across several messages. In general this is not a big issue because subsequent Ping packages
|
||||
// have the same size but a very big publish package with 100 MB of payload will increase the buffer
|
||||
// a lot and the size will never reduced. So this method tries to find a size which can be held in
|
||||
// memory for a long time without causing troubles.
|
||||
|
||||
if (_buffer.Length <= _maxBufferSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new and empty buffer. Do not use Array.Resize because it will copy all data from
|
||||
// the old array to the new one which is not required in this case.
|
||||
_buffer = new byte[_maxBufferSize];
|
||||
}
|
||||
|
||||
public byte[] GetBuffer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public static int GetLengthOfVariableInteger(uint value)
|
||||
{
|
||||
var result = 0;
|
||||
var x = value;
|
||||
do
|
||||
{
|
||||
x /= 128;
|
||||
result++;
|
||||
} while (x > 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Reset(int length)
|
||||
{
|
||||
_position = 0;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public void Seek(int position)
|
||||
{
|
||||
EnsureCapacity(position);
|
||||
_position = position;
|
||||
}
|
||||
|
||||
public void Write(MqttBufferWriter propertyWriter)
|
||||
{
|
||||
if (propertyWriter is MqttBufferWriter writer)
|
||||
{
|
||||
WriteBinary(writer._buffer, 0, writer.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
if (propertyWriter == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(propertyWriter));
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"{nameof(propertyWriter)} must be of type {nameof(MqttBufferWriter)}");
|
||||
}
|
||||
|
||||
public void WriteBinary(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureAdditionalCapacity(count);
|
||||
|
||||
Array.Copy(buffer, offset, _buffer, _position, count);
|
||||
IncreasePosition(count);
|
||||
}
|
||||
|
||||
public void WriteBinaryData(byte[] value)
|
||||
{
|
||||
if (value == null || value.Length == 0)
|
||||
{
|
||||
EnsureAdditionalCapacity(2);
|
||||
|
||||
_buffer[_position] = 0;
|
||||
_buffer[_position + 1] = 0;
|
||||
|
||||
IncreasePosition(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var valueLength = value.Length;
|
||||
|
||||
EnsureAdditionalCapacity(valueLength + 2);
|
||||
|
||||
_buffer[_position] = (byte)(valueLength >> 8);
|
||||
_buffer[_position + 1] = (byte)valueLength;
|
||||
|
||||
Array.Copy(value, 0, _buffer, _position + 2, valueLength);
|
||||
IncreasePosition(valueLength + 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteByte(byte @byte)
|
||||
{
|
||||
EnsureAdditionalCapacity(1);
|
||||
|
||||
_buffer[_position] = @byte;
|
||||
IncreasePosition(1);
|
||||
}
|
||||
|
||||
public void WriteString(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
EnsureAdditionalCapacity(2);
|
||||
|
||||
_buffer[_position] = 0;
|
||||
_buffer[_position + 1] = 0;
|
||||
|
||||
IncreasePosition(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do not use Encoding.UTF8.GetByteCount(value);
|
||||
// UTF8 chars can have a max length of 4 and the used buffer increase *2 every time.
|
||||
// So the buffer should always have much more capacity left so that a correct value
|
||||
// here is only waste of CPU cycles.
|
||||
var byteCount = value.Length * 4;
|
||||
|
||||
EnsureAdditionalCapacity(byteCount + 2);
|
||||
|
||||
var writtenBytes = Encoding.UTF8.GetBytes(value, 0, value.Length, _buffer, _position + 2);
|
||||
|
||||
_buffer[_position] = (byte)(writtenBytes >> 8);
|
||||
_buffer[_position + 1] = (byte)writtenBytes;
|
||||
|
||||
IncreasePosition(writtenBytes + 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteTwoByteInteger(ushort value)
|
||||
{
|
||||
EnsureAdditionalCapacity(2);
|
||||
|
||||
_buffer[_position] = (byte)(value >> 8);
|
||||
IncreasePosition(1);
|
||||
_buffer[_position] = (byte)value;
|
||||
IncreasePosition(1);
|
||||
}
|
||||
|
||||
public void WriteVariableByteInteger(uint value)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
_buffer[_position] = 0;
|
||||
IncreasePosition(1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (value <= 127)
|
||||
{
|
||||
_buffer[_position] = (byte)value;
|
||||
IncreasePosition(1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (value > VariableByteIntegerMaxValue)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"The specified value ({value}) is too large for a variable byte integer.");
|
||||
}
|
||||
|
||||
var size = 0;
|
||||
var x = value;
|
||||
do
|
||||
{
|
||||
var encodedByte = x % 128;
|
||||
x /= 128;
|
||||
if (x > 0)
|
||||
{
|
||||
encodedByte |= 128;
|
||||
}
|
||||
|
||||
_buffer[_position + size] = (byte)encodedByte;
|
||||
size++;
|
||||
} while (x > 0);
|
||||
|
||||
IncreasePosition(size);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void EnsureAdditionalCapacity(int additionalCapacity)
|
||||
{
|
||||
var bufferLength = _buffer.Length;
|
||||
|
||||
var freeSpace = bufferLength - _position;
|
||||
if (freeSpace >= additionalCapacity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureCapacity(bufferLength + additionalCapacity - freeSpace);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void EnsureCapacity(int capacity)
|
||||
{
|
||||
var newBufferLength = _buffer.Length;
|
||||
|
||||
if (newBufferLength >= capacity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (newBufferLength < capacity)
|
||||
{
|
||||
newBufferLength *= 2;
|
||||
}
|
||||
|
||||
// Array.Resize will create a new array and copy the existing
|
||||
// data to the new one.
|
||||
Array.Resize(ref _buffer, newBufferLength);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void IncreasePosition(int length)
|
||||
{
|
||||
_position += length;
|
||||
|
||||
if (_position > Length)
|
||||
{
|
||||
Length = _position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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 MQTTnet.Packets;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public sealed class MqttConnAckPacketFactory
|
||||
{
|
||||
public MqttConnAckPacket Create(ValidatingConnectionEventArgs validatingConnectionEventArgs)
|
||||
{
|
||||
if (validatingConnectionEventArgs == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validatingConnectionEventArgs));
|
||||
}
|
||||
|
||||
var connAckPacket = new MqttConnAckPacket
|
||||
{
|
||||
ReturnCode = MqttConnectReasonCodeConverter.ToConnectReturnCode(validatingConnectionEventArgs.ReasonCode),
|
||||
ReasonCode = validatingConnectionEventArgs.ReasonCode,
|
||||
RetainAvailable = true,
|
||||
SubscriptionIdentifiersAvailable = true,
|
||||
SharedSubscriptionAvailable = false,
|
||||
TopicAliasMaximum = ushort.MaxValue,
|
||||
WildcardSubscriptionAvailable = true,
|
||||
|
||||
AuthenticationMethod = validatingConnectionEventArgs.AuthenticationMethod,
|
||||
AuthenticationData = validatingConnectionEventArgs.ResponseAuthenticationData,
|
||||
AssignedClientIdentifier = validatingConnectionEventArgs.AssignedClientIdentifier,
|
||||
ReasonString = validatingConnectionEventArgs.ReasonString,
|
||||
ServerReference = validatingConnectionEventArgs.ServerReference,
|
||||
UserProperties = validatingConnectionEventArgs.ResponseUserProperties
|
||||
};
|
||||
|
||||
return connAckPacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public sealed class MqttConnectPacketFactory
|
||||
{
|
||||
public MqttConnectPacket Create(MqttClientOptions clientOptions)
|
||||
{
|
||||
if (clientOptions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(clientOptions));
|
||||
}
|
||||
|
||||
var connectPacket = new MqttConnectPacket
|
||||
{
|
||||
ClientId = clientOptions.ClientId,
|
||||
Username = clientOptions.Credentials?.GetUserName(clientOptions),
|
||||
Password = clientOptions.Credentials?.GetPassword(clientOptions),
|
||||
CleanSession = clientOptions.CleanSession,
|
||||
KeepAlivePeriod = (ushort)clientOptions.KeepAlivePeriod.TotalSeconds,
|
||||
AuthenticationMethod = clientOptions.AuthenticationMethod,
|
||||
AuthenticationData = clientOptions.AuthenticationData,
|
||||
WillDelayInterval = clientOptions.WillDelayInterval,
|
||||
MaximumPacketSize = clientOptions.MaximumPacketSize,
|
||||
ReceiveMaximum = clientOptions.ReceiveMaximum,
|
||||
RequestProblemInformation = clientOptions.RequestProblemInformation,
|
||||
RequestResponseInformation = clientOptions.RequestResponseInformation,
|
||||
SessionExpiryInterval = clientOptions.SessionExpiryInterval,
|
||||
TopicAliasMaximum = clientOptions.TopicAliasMaximum,
|
||||
UserProperties = clientOptions.UserProperties
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(clientOptions.WillTopic))
|
||||
{
|
||||
connectPacket.WillFlag = true;
|
||||
connectPacket.WillTopic = clientOptions.WillTopic;
|
||||
connectPacket.WillQoS = clientOptions.WillQualityOfServiceLevel;
|
||||
connectPacket.WillMessage = clientOptions.WillPayload;
|
||||
connectPacket.WillRetain = clientOptions.WillRetain;
|
||||
connectPacket.WillDelayInterval = clientOptions.WillDelayInterval;
|
||||
connectPacket.WillContentType = clientOptions.WillContentType;
|
||||
connectPacket.WillCorrelationData = clientOptions.WillCorrelationData;
|
||||
connectPacket.WillResponseTopic = clientOptions.WillResponseTopic;
|
||||
connectPacket.WillMessageExpiryInterval = clientOptions.WillMessageExpiryInterval;
|
||||
connectPacket.WillPayloadFormatIndicator = clientOptions.WillPayloadFormatIndicator;
|
||||
connectPacket.WillUserProperties = clientOptions.WillUserProperties;
|
||||
}
|
||||
|
||||
return connectPacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public static class MqttConnectReasonCodeConverter
|
||||
{
|
||||
public static MqttConnectReasonCode ToConnectReasonCode(MqttConnectReturnCode returnCode)
|
||||
{
|
||||
switch (returnCode)
|
||||
{
|
||||
case MqttConnectReturnCode.ConnectionAccepted:
|
||||
{
|
||||
return MqttConnectReasonCode.Success;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion:
|
||||
{
|
||||
return MqttConnectReasonCode.UnsupportedProtocolVersion;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword:
|
||||
{
|
||||
return MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedIdentifierRejected:
|
||||
{
|
||||
return MqttConnectReasonCode.ClientIdentifierNotValid;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedServerUnavailable:
|
||||
{
|
||||
return MqttConnectReasonCode.ServerUnavailable;
|
||||
}
|
||||
|
||||
case MqttConnectReturnCode.ConnectionRefusedNotAuthorized:
|
||||
{
|
||||
return MqttConnectReasonCode.NotAuthorized;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MqttConnectReturnCode ToConnectReturnCode(MqttConnectReasonCode reasonCode)
|
||||
{
|
||||
switch (reasonCode)
|
||||
{
|
||||
case MqttConnectReasonCode.Success:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionAccepted;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.NotAuthorized:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedNotAuthorized;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.BadUserNameOrPassword:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.ClientIdentifierNotValid:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.UnsupportedProtocolVersion:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.ServerUnavailable:
|
||||
case MqttConnectReasonCode.ServerBusy:
|
||||
case MqttConnectReasonCode.ServerMoved:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedServerUnavailable;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3).");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
public sealed class MqttDisconnectPacketFactory
|
||||
{
|
||||
readonly MqttDisconnectPacket _normalDisconnection = new MqttDisconnectPacket
|
||||
{
|
||||
ReasonCode = MqttDisconnectReasonCode.NormalDisconnection
|
||||
};
|
||||
|
||||
public MqttDisconnectPacket Create(MqttDisconnectReasonCode reasonCode)
|
||||
{
|
||||
if (reasonCode == MqttDisconnectReasonCode.NormalDisconnection)
|
||||
{
|
||||
return _normalDisconnection;
|
||||
}
|
||||
|
||||
return new MqttDisconnectPacket
|
||||
{
|
||||
ReasonCode = reasonCode
|
||||
};
|
||||
}
|
||||
|
||||
public MqttDisconnectPacket Create(MqttClientDisconnectOptions clientDisconnectOptions)
|
||||
{
|
||||
var packet = new MqttDisconnectPacket();
|
||||
|
||||
if (clientDisconnectOptions == null)
|
||||
{
|
||||
packet.ReasonCode = MqttDisconnectReasonCode.NormalDisconnection;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.ReasonCode = (MqttDisconnectReasonCode)clientDisconnectOptions.Reason;
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user