public override async ValueTask <ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default) { try { var stream = await _connection.AcceptStreamAsync(cancellationToken); QuicStreamContext?context = null; // Only use pool for bidirectional streams. Just a handful of unidirecitonal // streams are created for a connection and they live for the lifetime of the connection. if (stream.CanRead && stream.CanWrite) { lock (_poolLock) { StreamPool.TryPop(out context); } } if (context == null) { context = new QuicStreamContext(this, _context); } else { context.ResetFeatureCollection(); context.ResetItems(); } context.Initialize(stream); context.Start(); _log.AcceptedStream(context); return(context); } catch (QuicConnectionAbortedException ex) { // Shutdown initiated by peer, abortive. _log.ConnectionAborted(this, ex); ThreadPool.UnsafeQueueUserWorkItem(state => { state.CancelConnectionClosedToken(); }, this, preferLocal: false); } catch (QuicOperationAbortedException) { // Shutdown initiated by us // Allow for graceful closure. } return(null); }
public override async ValueTask <ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default) { try { var stream = await _connection.AcceptStreamAsync(cancellationToken); QuicStreamContext?context = null; // Only use pool for bidirectional streams. Just a handful of unidirecitonal // streams are created for a connection and they live for the lifetime of the connection. if (stream.CanRead && stream.CanWrite) { lock (_poolLock) { StreamPool.TryPop(out context); } } if (context == null) { context = new QuicStreamContext(this, _context); } else { context.ResetFeatureCollection(); context.ResetItems(); } context.Initialize(stream); context.Start(); QuicLog.AcceptedStream(_log, context); return(context); } catch (QuicConnectionAbortedException ex) { // Shutdown initiated by peer, abortive. _error = ex.ErrorCode; QuicLog.ConnectionAborted(_log, this, ex.ErrorCode, ex); ThreadPool.UnsafeQueueUserWorkItem(state => { state.CancelConnectionClosedToken(); }, this, preferLocal: false); // Throw error so consumer sees the connection is aborted by peer. throw new ConnectionResetException(ex.Message, ex); } catch (QuicOperationAbortedException ex) { lock (_shutdownLock) { // This error should only happen when shutdown has been initiated by the server. // If there is no abort reason and we have this error then the connection is in an // unexpected state. Abort connection and throw reason error. if (_abortReason == null) { Abort(new ConnectionAbortedException("Unexpected error when accepting stream.", ex)); } _abortReason !.Throw(); } } catch (OperationCanceledException) { Debug.Assert(cancellationToken.IsCancellationRequested, "Error requires cancellation is requested."); lock (_shutdownLock) { // Connection has been aborted. Throw reason exception. _abortReason?.Throw(); } } catch (Exception ex) { Debug.Fail($"Unexpected exception in {nameof(QuicConnectionContext)}.{nameof(AcceptAsync)}: {ex}"); throw; } // Return null for graceful closure or cancellation. return(null); }