Example #1
0
        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);
        }
Example #2
0
        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);
        }