internal async Task TimerLoopAsync(TimerAwaitable timer) { timer.Start(); using (timer) { // await returns True until `timer.Stop()` is called in the `finally` block of `ReceiveLoop` while (await timer) { try { // Ping server var response = await _session.SendRequestAsync(StreamingRequest.CreateGet("/api/version"), _disconnectCts.Token).ConfigureAwait(false); if (!IsSuccessResponse(response)) { Log.ClientKeepAliveFail(_logger, _url, response.StatusCode); IsConnected = false; Disconnected?.Invoke(this, new DisconnectedEventArgs() { Reason = $"Received failure from server heartbeat: {response.StatusCode}." }); } else { Log.ClientKeepAliveSucceed(_logger, _url); } } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception e) #pragma warning restore CA1031 // Do not catch general exception types { Log.ClientKeepAliveFail(_logger, _url, 0, e); IsConnected = false; Disconnected?.Invoke(this, new DisconnectedEventArgs() { Reason = $"Received failure from server heartbeat: {e}." }); } } } }
internal async Task ConnectInternalAsync(Func <WebSocketTransport, Task> connectFunc, CancellationToken cancellationToken) { CheckDisposed(); TimerAwaitable timer = null; Task timerTask = null; try { // Pipes _duplexPipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); // Transport var transport = new WebSocketTransport(_duplexPipePair.Application, _logger); // Application _transportHandler = new TransportHandler(_duplexPipePair.Transport, _logger); // Session _session = new StreamingSession(_requestHandler, _transportHandler, _logger, cancellationToken); // Set up cancellation _disconnectCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); // Start transport and application var transportTask = connectFunc(transport); var applicationTask = _transportHandler.ListenAsync(_disconnectCts.Token); var combinedTask = Task.WhenAll(transportTask, applicationTask); Log.ClientStarted(_logger, _url ?? string.Empty); // Periodic task: keep alive // Disposed with `timer.Stop()` in the finally block below if (_keepAlive.HasValue) { timer = new TimerAwaitable(_keepAlive.Value, _keepAlive.Value); timerTask = TimerLoopAsync(timer); } // We are connected! IsConnected = true; // Block until transport or application ends. await combinedTask.ConfigureAwait(false); // Signal that we're done _disconnectCts.Cancel(); Log.ClientTransportApplicationCompleted(_logger, _url); } finally { timer?.Stop(); if (timerTask != null) { await timerTask.ConfigureAwait(false); } } Log.ClientCompleted(_logger, _url ?? string.Empty); }