public async Task OpenAsync()
        {
            streamRecoveredSignal = new TaskCompletionSource <EventMessageStream <T> >(TaskCreationOptions.RunContinuationsAsynchronously);
            // streamRecoverTimeoutSignal = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            EventMessageStream <T> newStream = null;

            newStream = ActiveStream = getStreamDelegate((innerEx) => HandleUnexpectedCloseAsync(newStream, innerEx));
            await ActiveStream.OpenAsync().ConfigureAwait(false);

            await openDelegate(ActiveStream).ConfigureAwait(false);
        }
        private ValueTask HandleUnexpectedCloseAsync(EventMessageStream <T> oldStream, Exception ex)
        {
            // we have to run this off thread because we will block the old streams close method.
            Task.Run(async() =>
            {
                oldStream.Logger?.LogError(ex, "MessageStream errored. Attempting to recover...");

                try
                {
                    await closeDelegate(oldStream).ConfigureAwait(false);
                    await Task.WhenAny(Task.Delay(oldStreamCloseTimeout), oldStream.CloseAsync()).ConfigureAwait(false);
                }
                catch (Exception closeEx)
                {
                    oldStream.Logger?.LogError(closeEx, "Error closing old message stream");
                }

                bool success = false;
                EventMessageStream <T> newStream = null;
                while (!success && numReconnectAttempts < maxReconnectAttempts)
                {
                    try
                    {
                        await Task.Delay((int)(numReconnectAttempts * reconnectBackoff.TotalMilliseconds)).ConfigureAwait(false);
                        numReconnectAttempts++;
                        // we dont want to handle the disconnects until we've successfully reopened
                        newStream = getStreamDelegate((innerEx) => success ? HandleUnexpectedCloseAsync(newStream, innerEx) : new ValueTask());
                        await newStream.OpenAsync().ConfigureAwait(false);
                        await openDelegate(newStream).ConfigureAwait(false);
                        // Success
                        numReconnectAttempts = 0;
                        success = true;
                    }
                    catch (Exception reconnectEx)
                    {
                        // We override the orig exception with the latest one
                        oldStream.Logger?.LogError(ex = reconnectEx, $"Error recovering message stream after {numReconnectAttempts} attempts.");
                    }
                }

                // if we didnt succeed then close the new stream and send the disconnect event
                if (!success)
                {
                    streamRecoveredSignal.TrySetResult(null);
                    try
                    {
                        await closeDelegate(newStream).ConfigureAwait(false);
                        await Task.WhenAny(Task.Delay(oldStreamCloseTimeout), newStream.CloseAsync()).ConfigureAwait(false);
                        await unexpectedCloseDelegate(ex).ConfigureAwait(false);
                        oldStream.Logger?.LogWarning($"Closed message stream after {numReconnectAttempts} reconnection attempts.");
                    }
                    catch (Exception closeEx)
                    {
                        oldStream.Logger?.LogError(closeEx, "Error closing new message stream.");
                    }
                }
                else
                {
                    ActiveStream = newStream;
                    streamRecoveredSignal.TrySetResult(newStream);
                    streamRecoveredSignal = new TaskCompletionSource <EventMessageStream <T> >(TaskCreationOptions.RunContinuationsAsynchronously);
                    newStream.Logger?.LogInformation("MessageStream recovered.");
                }
            });
            return(new ValueTask());
        }