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; } }