This commit is contained in:
Christian
2022-02-20 21:10:46 +01:00
committed by GitHub
parent 47e674e4d3
commit 2ce9b94ec9
608 changed files with 22364 additions and 19420 deletions
@@ -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);
}
}
+7 -2
View File
@@ -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();
}
}
+206 -209
View File
@@ -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
@@ -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)
{
+11 -6
View File
@@ -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; }
}