SendHandshake(
            Uri uri,
            WebsocketSenderHandler websocketSenderHandler,
            CancellationToken ct,
            string?origin = null,
            IDictionary <string, string>?headers = null)
        {
            try
            {
                _connectionStatusAction(ConnectionStatus.SendingHandshakeToWebsocketServer, null);

                await websocketSenderHandler.SendConnectHandShake(
                    uri,
                    ct,
                    origin,
                    headers,
                    _websocketParserHandler.SubprotocolAcceptedNames);
            }
            catch (Exception ex)
            {
                return(
                    HandshakeStateKind.HandshakeSendFailed,
                    new WebsocketClientLiteException("Handshake send failed.", ex)
                    );
            }

            return(HandshakeStateKind.HandshakeSend, null);
        }
        internal IObservable <(HandshakeStateKind handshakeState, WebsocketClientLiteException?ex)> Handshake(
            Uri uri,
            WebsocketSenderHandler sender,
            TimeSpan timeout,
            CancellationToken ct,
            string?origin = null,
            IDictionary <string, string>?headers = null,
            IEnumerable <string>?subprotocols    = null)
        {
            return(Observable.Create <(HandshakeStateKind handshakeState, WebsocketClientLiteException?ex)>(async obs =>
            {
                using var parserDelegate = new HandshakeParserDelegate(obs);
                using var parserHandler = new HttpCombinedParser(parserDelegate);

                var handshakeParser = new HandshakeParser(
                    parserHandler,
                    parserDelegate,
                    _connectionStatusAction);

                await SendHandshake(uri, sender, ct, origin, headers);
                await WaitForHandshake(handshakeParser);

                obs.OnCompleted();
            })
                   .Timeout(timeout)
                   .Catch <
                       (HandshakeStateKind handshakeState, WebsocketClientLiteException?ex),
                       TimeoutException>(
                       tx => Observable.Return(
                           (HandshakeStateKind.HandshakeTimedOut,
                            new WebsocketClientLiteException("Handshake times out.", tx) ?? null)
                           )
                       ));

            async Task WaitForHandshake(HandshakeParser handshakeParser)
            {
                bool isHandshakeDone;

                do
                {
                    isHandshakeDone = await _tcpConnectionService
                                      .BytesObservable()
                                      .Select(b => handshakeParser.Parse(b, subprotocols));
                } while (!isHandshakeDone);
            }
        }
 internal async Task DisconnectWebsocket(
     WebsocketSenderHandler sender)
 {
     try
     {
         await sender.SendCloseHandshakeAsync(StatusCodes.GoingAway)
         .ToObservable()
         .Timeout(TimeSpan.FromSeconds(5));
     }
     catch (Exception ex)
     {
         throw new WebsocketClientLiteException("Unable to disconnect gracefully", ex);
     }
     finally
     {
         _connectionStatusAction(ConnectionStatus.Disconnected, null);
     }
 }
        internal async Task ConnectToWebSocketServer(
            WebsocketParserHandler websocketParserHandler,
            WebsocketSenderHandler websocketSenderHandler,
            Uri uri,
            bool secure,
            Stream tcpStream,
            string origin = null,
            IDictionary <string, string> headers = null,
            IEnumerable <string> subprotocols    = null)
        {
            _websocketParserHandler = websocketParserHandler;
            _websocketSenderHandler = websocketSenderHandler;

            TcpStream = tcpStream;

            _observerConnectionStatus.OnNext(ConnectionStatus.HandshakeSendToWebsocketServer);

            await SendConnectHandShakeAsync(uri, secure, origin, headers, subprotocols);

            var waitForHandShakeResult = await _websocketParserHandler.ParserDelegate
                                         .HandshakeParserCompletionObservable
                                         .Timeout(TimeSpan.FromSeconds(30))
                                         .Catch <ParserState, TimeoutException>(tx => Observable.Return(ParserState.HandshakeTimedOut));

            switch (waitForHandShakeResult)
            {
            case ParserState.HandshakeCompletedSuccessfully:
                _observerConnectionStatus.OnNext(ConnectionStatus.HandshakeCompletedSuccessfully);
                break;

            case ParserState.HandshakeFailed:
                throw new WebsocketClientLiteException("Unable to complete handshake");

            case ParserState.HandshakeTimedOut:
                throw new WebsocketClientLiteException("Handshake timed out.");

            default:
                throw new ArgumentOutOfRangeException($"Unknown parser state: {waitForHandShakeResult}");
            }
        }