From ed7b4e634cb794a045687dbd2a9d3d04ec37280b Mon Sep 17 00:00:00 2001 From: Luke Bakken Date: Mon, 4 Dec 2023 12:01:54 -0800 Subject: [PATCH] Pass exception back up to application code --- .../client/api/ShutdownEventArgs.cs | 34 ++++++++++++++--- .../RabbitMQ.Client/client/impl/Connection.cs | 37 +++++++++++++------ .../RabbitMQ.Client/client/impl/ModelBase.cs | 17 ++++++++- .../Unit/APIApproval.Approve.verified.txt | 5 ++- 4 files changed, 72 insertions(+), 21 deletions(-) diff --git a/projects/RabbitMQ.Client/client/api/ShutdownEventArgs.cs b/projects/RabbitMQ.Client/client/api/ShutdownEventArgs.cs index bf98a1882a..961c4a74ed 100644 --- a/projects/RabbitMQ.Client/client/api/ShutdownEventArgs.cs +++ b/projects/RabbitMQ.Client/client/api/ShutdownEventArgs.cs @@ -41,12 +41,14 @@ namespace RabbitMQ.Client /// public class ShutdownEventArgs : EventArgs { + private readonly Exception _exception; + /// /// Construct a with the given parameters and /// 0 for and . /// - public ShutdownEventArgs(ShutdownInitiator initiator, ushort replyCode, string replyText, object cause = null) - : this(initiator, replyCode, replyText, 0, 0, cause) + public ShutdownEventArgs(ShutdownInitiator initiator, ushort replyCode, string replyText, object cause = null, Exception exception = null) + : this(initiator, replyCode, replyText, 0, 0, cause, exception) { } @@ -54,7 +56,7 @@ public ShutdownEventArgs(ShutdownInitiator initiator, ushort replyCode, string r /// Construct a with the given parameters. /// public ShutdownEventArgs(ShutdownInitiator initiator, ushort replyCode, string replyText, - ushort classId, ushort methodId, object cause = null) + ushort classId, ushort methodId, object cause = null, Exception exception = null) { Initiator = initiator; ReplyCode = replyCode; @@ -62,6 +64,25 @@ public ShutdownEventArgs(ShutdownInitiator initiator, ushort replyCode, string r ClassId = classId; MethodId = methodId; Cause = cause; + _exception = exception; + } + + /// + /// Exception causing the shutdown, or null if none. + /// + public Exception Exception + { + get + { + if (_exception != null) + { + return _exception; + } + else + { + return new Exception(ToString()); + } + } } /// @@ -98,13 +119,14 @@ public ShutdownEventArgs(ShutdownInitiator initiator, ushort replyCode, string r /// Override ToString to be useful for debugging. /// public override string ToString() - { - return $"AMQP close-reason, initiated by {Initiator}" + { + return $"AMQP close-reason, initiated by {Initiator}" + $", code={ReplyCode}" + (ReplyText != null ? $", text='{ReplyText}'" : string.Empty) + $", classId={ClassId}" + $", methodId={MethodId}" - + (Cause != null ? $", cause={Cause}" : string.Empty); + + (Cause != null ? $", cause={Cause}" : string.Empty) + + (_exception != null ? $", exception={_exception}" : string.Empty); } } } diff --git a/projects/RabbitMQ.Client/client/impl/Connection.cs b/projects/RabbitMQ.Client/client/impl/Connection.cs index 8f315b8fc3..19d105c899 100644 --- a/projects/RabbitMQ.Client/client/impl/Connection.cs +++ b/projects/RabbitMQ.Client/client/impl/Connection.cs @@ -457,13 +457,14 @@ public void HandleMainLoopException(ShutdownEventArgs reason) { if (!SetCloseReason(reason)) { - LogCloseError("Unexpected Main Loop Exception while closing: " - + reason, new Exception(reason.ToString())); + LogCloseError($"Unexpected Main Loop Exception while closing: {reason}", reason.Exception); return; } + _model0.MaybeSetConnectionStartException(reason.Exception); + OnShutdown(); - LogCloseError($"Unexpected connection closure: {reason}", new Exception(reason.ToString())); + LogCloseError($"Unexpected connection closure: {reason}", reason.Exception); } public bool HardProtocolExceptionHandler(HardProtocolException hpe) @@ -540,22 +541,33 @@ public void MainLoop() catch (EndOfStreamException eose) { // Possible heartbeat exception - HandleMainLoopException(new ShutdownEventArgs( - ShutdownInitiator.Library, - 0, - "End of stream", - eose)); + var ea = new ShutdownEventArgs(ShutdownInitiator.Library, + 0, "End of stream", + cause: null, exception: eose); + HandleMainLoopException(ea); } catch (HardProtocolException hpe) { shutdownCleanly = HardProtocolExceptionHandler(hpe); } + catch (FileLoadException fileLoadException) + { + /* + * https://github.com/rabbitmq/rabbitmq-dotnet-client/issues/1434 + * Ensure that these exceptions eventually make it to application code + */ + var ea = new ShutdownEventArgs(ShutdownInitiator.Library, + Constants.InternalError, fileLoadException.Message, + cause: null, exception: fileLoadException); + HandleMainLoopException(ea); + } catch (Exception ex) { - HandleMainLoopException(new ShutdownEventArgs(ShutdownInitiator.Library, + var ea = new ShutdownEventArgs(ShutdownInitiator.Library, Constants.InternalError, - "Unexpected Exception", - ex)); + $"Unexpected Exception: {ex.Message}", + cause: null, exception: ex); + HandleMainLoopException(ea); } // If allowed for clean shutdown, run main loop until the @@ -1104,7 +1116,8 @@ private void StartAndTune() if (connectionStart == null) { - throw new IOException("connection.start was never received, likely due to a network timeout"); + const string msg = "connection.start was never received, likely due to a network timeout"; + throw new IOException(msg, _model0.ConnectionStartException); } ServerProperties = connectionStart.m_serverProperties; diff --git a/projects/RabbitMQ.Client/client/impl/ModelBase.cs b/projects/RabbitMQ.Client/client/impl/ModelBase.cs index 67f97f43bb..3bc28aacae 100644 --- a/projects/RabbitMQ.Client/client/impl/ModelBase.cs +++ b/projects/RabbitMQ.Client/client/impl/ModelBase.cs @@ -48,7 +48,9 @@ abstract class ModelBase : IFullModel, IRecoverable { ///Only used to kick-start a connection open ///sequence. See - public BlockingCell m_connectionStartCell = null; + internal BlockingCell m_connectionStartCell = null; + private Exception m_connectionStartException = null; + internal readonly IBasicProperties _emptyBasicProperties; private readonly Dictionary _consumers = new Dictionary(); @@ -174,6 +176,19 @@ public bool IsOpen public ISession Session { get; private set; } + public Exception ConnectionStartException + { + get { return m_connectionStartException; } + } + + public void MaybeSetConnectionStartException(Exception ex) + { + if (m_connectionStartCell != null) + { + m_connectionStartException = ex; + } + } + public Task Close(ushort replyCode, string replyText, bool abort) { return Close(new ShutdownEventArgs(ShutdownInitiator.Application, diff --git a/projects/Unit/APIApproval.Approve.verified.txt b/projects/Unit/APIApproval.Approve.verified.txt index 044048d3be..fdfda01b15 100644 --- a/projects/Unit/APIApproval.Approve.verified.txt +++ b/projects/Unit/APIApproval.Approve.verified.txt @@ -609,10 +609,11 @@ namespace RabbitMQ.Client } public class ShutdownEventArgs : System.EventArgs { - public ShutdownEventArgs(RabbitMQ.Client.ShutdownInitiator initiator, ushort replyCode, string replyText, object cause = null) { } - public ShutdownEventArgs(RabbitMQ.Client.ShutdownInitiator initiator, ushort replyCode, string replyText, ushort classId, ushort methodId, object cause = null) { } + public ShutdownEventArgs(RabbitMQ.Client.ShutdownInitiator initiator, ushort replyCode, string replyText, object cause = null, System.Exception exception = null) { } + public ShutdownEventArgs(RabbitMQ.Client.ShutdownInitiator initiator, ushort replyCode, string replyText, ushort classId, ushort methodId, object cause = null, System.Exception exception = null) { } public object Cause { get; } public ushort ClassId { get; } + public System.Exception Exception { get; } public RabbitMQ.Client.ShutdownInitiator Initiator { get; } public ushort MethodId { get; } public ushort ReplyCode { get; }