private async Task ConnectAsync(CancellationToken token)
        {
            try
            {
                await BackOff();

                _stateSubject.OnNext(GraphQLWebsocketConnectionState.Connecting);
                Debug.WriteLine($"opening websocket {_clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})");
                await _clientWebSocket.ConnectAsync(_webSocketUri, token);

                _stateSubject.OnNext(GraphQLWebsocketConnectionState.Connected);
                Debug.WriteLine($"connection established on websocket {_clientWebSocket.GetHashCode()}, invoking Options.OnWebsocketConnected()");
                await(Options.OnWebsocketConnected?.Invoke(_client) ?? Task.CompletedTask);
                Debug.WriteLine($"invoking Options.OnWebsocketConnected() on websocket {_clientWebSocket.GetHashCode()}");
                _connectionAttempt = 1;

                // create receiving observable
                _incomingMessages = Observable
                                    .Defer(() => GetReceiveTask().ToObservable().ObserveOn(_receiveLoopScheduler))
                                    .Repeat()
                                    // complete sequence on OperationCanceledException, this is triggered by the cancellation token on disposal
                                    .Catch <WebsocketMessageWrapper, OperationCanceledException>(exception => Observable.Empty <WebsocketMessageWrapper>())
                                    .Publish();

                // subscribe maintenance
                var maintenanceSubscription = _incomingMessages.Subscribe(_ => { }, ex =>
                {
                    Debug.WriteLine($"incoming message stream {_incomingMessages.GetHashCode()} received an error: {ex}");
                    _exceptionSubject.OnNext(ex);
                    _incomingMessagesConnection?.Dispose();
                    _stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected);
                },
                                                                          () =>
                {
                    Debug.WriteLine($"incoming message stream {_incomingMessages.GetHashCode()} completed");
                    _incomingMessagesConnection?.Dispose();
                    _stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected);
                });


                // connect observable
                var connection = _incomingMessages.Connect();
                Debug.WriteLine($"new incoming message stream {_incomingMessages.GetHashCode()} created");

                _incomingMessagesConnection = new CompositeDisposable(maintenanceSubscription, connection);
            }
            catch (Exception e)
            {
                _stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected);
                _exceptionSubject.OnNext(e);
                throw;
            }
        }
        private async Task ConnectAsync(CancellationToken token)
        {
            try
            {
                await BackOff();

                _stateSubject.OnNext(GraphQLWebsocketConnectionState.Connecting);
                Debug.WriteLine($"opening websocket {_clientWebSocket.GetHashCode()} (thread {Thread.CurrentThread.ManagedThreadId})");
                await _clientWebSocket.ConnectAsync(_webSocketUri, token);

                _stateSubject.OnNext(GraphQLWebsocketConnectionState.Connected);
                Debug.WriteLine($"connection established on websocket {_clientWebSocket.GetHashCode()}, invoking Options.OnWebsocketConnected()");
                await(Options.OnWebsocketConnected?.Invoke(_client) ?? Task.CompletedTask);
                Debug.WriteLine($"invoking Options.OnWebsocketConnected() on websocket {_clientWebSocket.GetHashCode()}");
                _connectionAttempt = 1;

                // create receiving observable
                _incomingMessages = Observable
                                    .Defer(() => GetReceiveTask().ToObservable())
                                    .Repeat()
                                    // complete sequence on OperationCanceledException, this is triggered by the cancellation token on disposal
                                    .Catch <WebsocketMessageWrapper, OperationCanceledException>(exception => Observable.Empty <WebsocketMessageWrapper>())
                                    .Publish();

                // subscribe maintenance
                var maintenanceSubscription = _incomingMessages.Subscribe(_ => { }, ex =>
                {
                    Debug.WriteLine($"incoming message stream {_incomingMessages.GetHashCode()} received an error: {ex}");
                    _exceptionSubject.OnNext(ex);
                    _incomingMessagesConnection?.Dispose();
                    _stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected);
                },
                                                                          () =>
                {
                    Debug.WriteLine($"incoming message stream {_incomingMessages.GetHashCode()} completed");
                    _incomingMessagesConnection?.Dispose();
                    _stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected);
                });


                // connect observable
                var connection = _incomingMessages.Connect();
                Debug.WriteLine($"new incoming message stream {_incomingMessages.GetHashCode()} created");

                _incomingMessagesConnection = new CompositeDisposable(maintenanceSubscription, connection);

                var initRequest = new GraphQLWebSocketRequest
                {
                    Type    = GraphQLWebSocketMessageType.GQL_CONNECTION_INIT,
                    Payload = Options.ConfigureWebSocketConnectionInitPayload(Options)
                };

                // setup task to await connection_ack message
                var ackTask = _incomingMessages
                              .Where(response => response != null)
                              .TakeUntil(response => response.Type == GraphQLWebSocketMessageType.GQL_CONNECTION_ACK ||
                                         response.Type == GraphQLWebSocketMessageType.GQL_CONNECTION_ERROR)
                              .FirstAsync()
                              .ToTask();

                // send connection init
                Debug.WriteLine($"sending connection init message");
                await SendWebSocketMessageAsync(initRequest);

                var response = await ackTask;

                if (response.Type == GraphQLWebSocketMessageType.GQL_CONNECTION_ACK)
                {
                    Debug.WriteLine($"connection acknowledged: {Encoding.UTF8.GetString(response.MessageBytes)}");
                }
                else
                {
                    var errorPayload = Encoding.UTF8.GetString(response.MessageBytes);
                    Debug.WriteLine($"connection error received: {errorPayload}");
                    throw new GraphQLWebsocketConnectionException(errorPayload);
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine($"failed to establish websocket connection");
                _stateSubject.OnNext(GraphQLWebsocketConnectionState.Disconnected);
                _exceptionSubject.OnNext(e);
                throw;
            }
        }