public override async ValueTask <ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default) { try { var stream = await _connection.AcceptStreamAsync(cancellationToken); var context = new QuicStreamContext(stream, this, _context); 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(); _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(); _log.AcceptedStream(context); return(context); } catch (QuicConnectionAbortedException ex) { // Shutdown initiated by peer, abortive. _error = ex.ErrorCode; _log.ConnectionAborted(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) { // Shutdown initiated by us. lock (_shutdownLock) { // Connection has been aborted. Throw reason exception. _abortReason?.Throw(); } } catch (OperationCanceledException) { // Shutdown initiated by us. 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); }