From abbf14b1eca1941c70112dd4cc32f24e55c3797b Mon Sep 17 00:00:00 2001 From: fazho <96215785+fazho@users.noreply.github.com> Date: Tue, 7 May 2024 09:31:16 -0700 Subject: [PATCH] =?UTF-8?q?rethrow=20MqttClientUnexpectedDisconnectReceive?= =?UTF-8?q?dException=20when=20Publish=20=E2=80=A6=20(#1974)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * rethrow MqttClientUnexpectedDisconnectReceivedException when Publish errors out because server disconnects the client and QoS is 0 * use a local var to cache _unexpectedDisconnectPacket * Add inner exception to new exception * Update release notes * set the default inner exception as null --------- Co-authored-by: christian <6939810+chkr1011@users.noreply.github.com> --- .github/workflows/ReleaseNotes.md | 1 + ...ntUnexpectedDisconnectReceivedException.cs | 16 +++++---- Source/MQTTnet/Client/MqttClient.cs | 35 +++++++++++++++---- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ReleaseNotes.md b/.github/workflows/ReleaseNotes.md index e9ae9cd1..ecac6db0 100644 --- a/.github/workflows/ReleaseNotes.md +++ b/.github/workflows/ReleaseNotes.md @@ -1,6 +1,7 @@ * [Core] Optimized packet serialization of PUBACK and PUBREC packets for protocol version 5.0.0 (#1939, thanks to @Y-Sindo). * [Core] The package inspector is now fully async (#1941). * [Client] Added a dedicated exception when the client is not connected (#1954, thanks to @marcpiulachs). +* [Client] The client will now throw a _MqttClientUnexpectedDisconnectReceivedException_ when publishing a QoS 0 message which leads to a server disconnect (BREAKING CHANGE!, #1974, thanks to @fazho). * [Client] Exposed the certificate selection event handler in client options (#1984). * [Server] The server will no longer send _NoMatchingSubscribers_ when the actual subscription was non success (#1965, BREAKING CHANGE!). * [Server] Fixed broken support for _null_ in _AddServer_ method in ASP.NET integration (#1981). diff --git a/Source/MQTTnet/Client/Exceptions/MqttClientUnexpectedDisconnectReceivedException.cs b/Source/MQTTnet/Client/Exceptions/MqttClientUnexpectedDisconnectReceivedException.cs index 5501e39c..57cfeafe 100644 --- a/Source/MQTTnet/Client/Exceptions/MqttClientUnexpectedDisconnectReceivedException.cs +++ b/Source/MQTTnet/Client/Exceptions/MqttClientUnexpectedDisconnectReceivedException.cs @@ -2,6 +2,7 @@ // 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; @@ -11,8 +12,9 @@ namespace MQTTnet.Client { public sealed class MqttClientUnexpectedDisconnectReceivedException : MqttCommunicationException { - public MqttClientUnexpectedDisconnectReceivedException(MqttDisconnectPacket disconnectPacket) - : base($"Unexpected DISCONNECT (Reason code={disconnectPacket.ReasonCode}) received.") + public MqttClientUnexpectedDisconnectReceivedException(MqttDisconnectPacket disconnectPacket, Exception innerExcpetion = null) : base( + $"Unexpected DISCONNECT (Reason code={disconnectPacket.ReasonCode}) received.", + innerExcpetion) { ReasonCode = disconnectPacket.ReasonCode; SessionExpiryInterval = disconnectPacket.SessionExpiryInterval; @@ -23,12 +25,12 @@ namespace MQTTnet.Client public MqttDisconnectReasonCode? ReasonCode { get; } - public uint? SessionExpiryInterval { get; } - public string ReasonString { get; } - public List UserProperties { get; } - public string ServerReference { get; } + + public uint? SessionExpiryInterval { get; } + + public List UserProperties { get; } } -} +} \ No newline at end of file diff --git a/Source/MQTTnet/Client/MqttClient.cs b/Source/MQTTnet/Client/MqttClient.cs index 2cb737f2..fc01bf75 100644 --- a/Source/MQTTnet/Client/MqttClient.cs +++ b/Source/MQTTnet/Client/MqttClient.cs @@ -48,6 +48,7 @@ namespace MQTTnet.Client Task _packetReceiverTask; AsyncQueue _publishPacketReceiverQueue; Task _publishPacketReceiverTask; + MqttDisconnectPacket _unexpectedDisconnectPacket; public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger) { @@ -123,6 +124,8 @@ namespace MQTTnet.Client var adapter = _adapterFactory.CreateClientAdapter(options, new MqttPacketInspector(_events.InspectPacketEvent, _rootLogger), _rootLogger); _adapter = adapter ?? throw new InvalidOperationException("The adapter factory did not provide an adapter."); + _unexpectedDisconnectPacket = null; + if (cancellationToken.CanBeCanceled) { connectResult = await ConnectInternal(adapter, cancellationToken).ConfigureAwait(false); @@ -239,7 +242,7 @@ namespace MQTTnet.Client public async Task PingAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); - + ThrowIfDisposed(); ThrowIfNotConnected(); @@ -330,7 +333,7 @@ namespace MQTTnet.Client ThrowIfDisposed(); ThrowIfNotConnected(); - + if (Options.ValidateFeatures) { MqttClientSubscribeOptionsValidator.ThrowIfNotSupported(options, _adapter.PacketFormatterAdapter.ProtocolVersion); @@ -682,9 +685,10 @@ namespace MQTTnet.Client _disconnectReason = (int)disconnectPacket.ReasonCode; _disconnectReasonString = disconnectPacket.ReasonString; _disconnectUserProperties = disconnectPacket.UserProperties; + _unexpectedDisconnectPacket = disconnectPacket; // Also dispatch disconnect to waiting threads to generate a proper exception. - _packetDispatcher.Dispose(new MqttClientUnexpectedDisconnectReceivedException(disconnectPacket)); + _packetDispatcher.Dispose(new MqttClientUnexpectedDisconnectReceivedException(disconnectPacket, null)); return DisconnectInternal(_packetReceiverTask, null, null); } @@ -751,10 +755,27 @@ namespace MQTTnet.Client async Task PublishAtMostOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken) { - // No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier] - await Send(publishPacket, cancellationToken).ConfigureAwait(false); + try + { + // No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier] + await Send(publishPacket, cancellationToken).ConfigureAwait(false); - return MqttClientResultFactory.PublishResult.Create(null); + return MqttClientResultFactory.PublishResult.Create(null); + } + catch (Exception exception) + { + // We have to check if the server has sent a disconnect packet in response to the published message. + // Since we are in QoS 0 we do not get a direct response via an PUBACK packet and thus basically no + // feedback at all. + var localUnexpectedDisconnectPacket = _unexpectedDisconnectPacket; + + if (localUnexpectedDisconnectPacket != null) + { + throw new MqttClientUnexpectedDisconnectReceivedException(localUnexpectedDisconnectPacket, exception); + } + + throw; + } } async Task PublishExactlyOnce(MqttPublishPacket publishPacket, CancellationToken cancellationToken) @@ -1071,4 +1092,4 @@ namespace MQTTnet.Client } } } -} +} \ No newline at end of file