diff --git a/projects/RabbitMQ.Client/client/api/ShutdownEventArgs.cs b/projects/RabbitMQ.Client/client/api/ShutdownEventArgs.cs
index 22919dc0a4..985a989c91 100644
--- a/projects/RabbitMQ.Client/client/api/ShutdownEventArgs.cs
+++ b/projects/RabbitMQ.Client/client/api/ShutdownEventArgs.cs
@@ -41,11 +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)
+ public ShutdownEventArgs(ShutdownInitiator initiator, ushort replyCode, string replyText,
+ object cause = null)
: this(initiator, replyCode, replyText, 0, 0, cause)
{
}
@@ -64,6 +67,26 @@ public ShutdownEventArgs(ShutdownInitiator initiator, ushort replyCode, string r
Cause = cause;
}
+ ///
+ /// Construct a with the given parameters.
+ ///
+ public ShutdownEventArgs(ShutdownInitiator initiator, ushort replyCode, string replyText, Exception exception)
+ : this(initiator, replyCode, replyText, 0, 0)
+ {
+ _exception = exception ?? throw new ArgumentNullException(nameof(exception));
+ }
+
+ ///
+ /// Exception causing the shutdown, or null if none.
+ ///
+ public Exception Exception
+ {
+ get
+ {
+ return _exception;
+ }
+ }
+
///
/// Object causing the shutdown, or null if none.
///
@@ -104,7 +127,8 @@ public override string ToString()
+ (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/ChannelBase.cs b/projects/RabbitMQ.Client/client/impl/ChannelBase.cs
index 4c9d965fd5..79be88582c 100644
--- a/projects/RabbitMQ.Client/client/impl/ChannelBase.cs
+++ b/projects/RabbitMQ.Client/client/impl/ChannelBase.cs
@@ -50,6 +50,7 @@ internal abstract class ChannelBase : IChannel, IRecoverable
///Only used to kick-start a connection open
///sequence. See
internal TaskCompletionSource m_connectionStartCell;
+ private Exception m_connectionStartException = null;
// AMQP only allows one RPC operation to be active at a time.
protected readonly SemaphoreSlim _rpcSemaphore = new SemaphoreSlim(1, 1);
@@ -171,6 +172,16 @@ public IBasicConsumer DefaultConsumer
public ISession Session { get; private set; }
+ public Exception ConnectionStartException => m_connectionStartException;
+
+ public void MaybeSetConnectionStartException(Exception ex)
+ {
+ if (m_connectionStartCell != null)
+ {
+ m_connectionStartException = ex;
+ }
+ }
+
protected void TakeOver(ChannelBase other)
{
_basicAcksWrapper.Takeover(other._basicAcksWrapper);
diff --git a/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs b/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs
index d277026d76..172c899a93 100644
--- a/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs
+++ b/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs
@@ -81,7 +81,8 @@ await _frameHandler.SendProtocolHeaderAsync()
if (connectionStart is 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, _channel0.ConnectionStartException);
}
ServerProperties = connectionStart.m_serverProperties;
diff --git a/projects/RabbitMQ.Client/client/impl/Connection.Receive.cs b/projects/RabbitMQ.Client/client/impl/Connection.Receive.cs
index 777750fd9f..282ac29ea3 100644
--- a/projects/RabbitMQ.Client/client/impl/Connection.Receive.cs
+++ b/projects/RabbitMQ.Client/client/impl/Connection.Receive.cs
@@ -53,16 +53,34 @@ await ReceiveLoop()
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",
+ exception: eose);
+ HandleMainLoopException(ea);
}
catch (HardProtocolException hpe)
{
await HardProtocolExceptionHandler(hpe)
.ConfigureAwait(false);
}
+ 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,
+ exception: fileLoadException);
+ HandleMainLoopException(ea);
+ }
catch (Exception ex)
{
- HandleMainLoopException(new ShutdownEventArgs(ShutdownInitiator.Library, Constants.InternalError, "Unexpected Exception", ex));
+ var ea = new ShutdownEventArgs(ShutdownInitiator.Library,
+ Constants.InternalError,
+ $"Unexpected Exception: {ex.Message}",
+ exception: ex);
+ HandleMainLoopException(ea);
}
FinishClose();
@@ -146,15 +164,17 @@ private void HandleMainLoopException(ShutdownEventArgs reason)
{
if (!SetCloseReason(reason))
{
- // TODO reason.Cause could be an Exception, should we use that?
- LogCloseError("Unexpected Main Loop Exception while closing: " + reason, new Exception(reason.ToString()));
+ LogCloseError($"Unexpected Main Loop Exception while closing: {reason}", reason.Exception);
return;
}
+ _channel0.MaybeSetConnectionStartException(reason.Exception);
+
OnShutdown(reason);
- LogCloseError($"Unexpected connection closure: {reason}", new Exception(reason.ToString()));
+ LogCloseError($"Unexpected connection closure: {reason}", reason.Exception);
}
+ // TODO rename Async, add cancellation token?
private async Task HardProtocolExceptionHandler(HardProtocolException hpe)
{
if (SetCloseReason(hpe.ShutdownReason))
diff --git a/projects/Test/Unit/APIApproval.Approve.verified.txt b/projects/Test/Unit/APIApproval.Approve.verified.txt
index 1d9773af41..a3d25697c1 100644
--- a/projects/Test/Unit/APIApproval.Approve.verified.txt
+++ b/projects/Test/Unit/APIApproval.Approve.verified.txt
@@ -787,10 +787,12 @@ namespace RabbitMQ.Client
}
public class ShutdownEventArgs : System.EventArgs
{
+ public ShutdownEventArgs(RabbitMQ.Client.ShutdownInitiator initiator, ushort replyCode, string replyText, System.Exception exception) { }
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 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; }