/// <summary> /// Opens the <see cref="StreamingConnection"/> and listens for incoming requests, which will /// be assembled and sent to the provided <see cref="RequestHandler"/>. /// </summary> /// <param name="requestHandler"><see cref="RequestHandler"/> to which incoming requests will be sent.</param> /// <param name="cancellationToken"><see cref="CancellationToken"/> that signals the need to stop the connection. /// Once the token is cancelled, the connection will be gracefully shut down, finishing pending sends and receives.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> public virtual async Task ListenAsync(RequestHandler requestHandler, CancellationToken cancellationToken = default) { if (requestHandler == null) { throw new ArgumentNullException(nameof(requestHandler)); } var duplexPipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); // Create transport and application _transport = CreateStreamingTransport(duplexPipePair.Application); var application = new TransportHandler(duplexPipePair.Transport, Logger); // Create session _session = new StreamingSession(requestHandler, application, Logger, cancellationToken); // Start transport and application var transportTask = _transport.ConnectAsync(cancellationToken); var applicationTask = application.ListenAsync(cancellationToken); var tasks = new List <Task> { transportTask, applicationTask }; // Signal that session is ready to be used _sessionInitializedTask.SetResult(true); // Let application and transport run await Task.WhenAll(tasks).ConfigureAwait(false); }
private async Task ConnectImplAsync(Func <StreamingTransport, Task> connectFunc, CancellationToken cancellationToken) { CheckDisposed(); TimerAwaitable timer = null; Task timerTask = null; try { // Pipes var duplexPipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); // Transport _transport = CreateStreamingTransport(duplexPipePair.Application); // Application _application = new TransportHandler(duplexPipePair.Transport, Logger); // Session _session = new StreamingSession(_requestHandler, _application, Logger, cancellationToken); // Set up cancellation _disconnectCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); // Start transport and application var transportTask = connectFunc(_transport); var applicationTask = _application.ListenAsync(_disconnectCts.Token); var combinedTask = Task.WhenAll(transportTask, applicationTask); Log.ClientStarted(Logger, _url); // 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); }