Example #1
0
 public void AcceptedStream(QuicStreamContext streamContext)
 {
     if (_logger.IsEnabled(LogLevel.Debug))
     {
         AcceptedStream(_logger, streamContext.ConnectionId, GetStreamType(streamContext));
     }
 }
Example #2
0
 public void StreamPause(QuicStreamContext streamContext)
 {
     if (_logger.IsEnabled(LogLevel.Debug))
     {
         StreamPause(_logger, streamContext.ConnectionId);
     }
 }
Example #3
0
 public void StreamError(QuicStreamContext streamContext, Exception ex)
 {
     if (_logger.IsEnabled(LogLevel.Debug))
     {
         _streamError(_logger, streamContext.ConnectionId, ex);
     }
 }
Example #4
0
 public static void StreamPause(ILogger logger, QuicStreamContext streamContext)
 {
     if (logger.IsEnabled(LogLevel.Debug))
     {
         StreamPauseCore(logger, streamContext.ConnectionId);
     }
 }
Example #5
0
 public void StreamAbort(QuicStreamContext streamContext, string reason)
 {
     if (_logger.IsEnabled(LogLevel.Debug))
     {
         _streamAborted(_logger, streamContext.ConnectionId, reason, null);
     }
 }
Example #6
0
 public void StreamAbortedWrite(QuicStreamContext streamContext, long errorCode)
 {
     if (_logger.IsEnabled(LogLevel.Debug))
     {
         StreamAbortedWrite(_logger, streamContext.ConnectionId, errorCode);
     }
 }
Example #7
0
 public static void StreamAbortedRead(ILogger logger, QuicStreamContext streamContext, long errorCode)
 {
     if (logger.IsEnabled(LogLevel.Debug))
     {
         StreamAbortedReadCore(logger, streamContext.ConnectionId, errorCode);
     }
 }
Example #8
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 #9
0
 public static void StreamShutdownWrite(ILogger logger, QuicStreamContext streamContext, string reason)
 {
     if (logger.IsEnabled(LogLevel.Debug))
     {
         StreamShutdownWriteCore(logger, streamContext.ConnectionId, reason);
     }
 }
Example #10
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();
                return(context);
            }
            catch (QuicConnectionAbortedException ex)
            {
                // Shutdown initiated by peer, abortive.
                // TODO cancel CTS here?
                _log.LogDebug($"Accept loop ended with exception: {ex.Message}");
            }
            catch (QuicOperationAbortedException)
            {
                // Shutdown initiated by us

                // Allow for graceful closure.
            }

            return(null);
        }
Example #11
0
        public override ValueTask <ConnectionContext> ConnectAsync(IFeatureCollection?features = null, CancellationToken cancellationToken = default)
        {
            QuicStream quicStream;

            var streamDirectionFeature = features?.Get <IStreamDirectionFeature>();

            if (streamDirectionFeature != null)
            {
                if (streamDirectionFeature.CanRead)
                {
                    quicStream = _connection.OpenBidirectionalStream();
                }
                else
                {
                    quicStream = _connection.OpenUnidirectionalStream();
                }
            }
            else
            {
                quicStream = _connection.OpenBidirectionalStream();
            }

            // Only a handful of control streams are created by the server and they last for the
            // lifetime of the connection. No value in pooling them.
            QuicStreamContext?context = new QuicStreamContext(this, _context);

            context.Initialize(quicStream);
            context.Start();

            QuicLog.ConnectedStream(_log, context);

            return(new ValueTask <ConnectionContext>(context));
        }
Example #12
0
 public void StreamAbortRead(QuicStreamContext streamContext, long errorCode, string reason)
 {
     if (_logger.IsEnabled(LogLevel.Debug))
     {
         StreamAbortRead(_logger, streamContext.ConnectionId, errorCode, reason);
     }
 }
Example #13
0
 public void StreamResume(QuicStreamContext streamContext)
 {
     if (_logger.IsEnabled(LogLevel.Debug))
     {
         _streamResume(_logger, streamContext.ConnectionId, null);
     }
 }
Example #14
0
 public void AcceptedStream(QuicStreamContext streamContext)
 {
     if (_logger.IsEnabled(LogLevel.Debug))
     {
         _acceptedStream(_logger, streamContext.ConnectionId, null);
     }
 }
Example #15
0
 public static void ConnectedStream(ILogger logger, QuicStreamContext streamContext)
 {
     if (logger.IsEnabled(LogLevel.Debug))
     {
         ConnectedStreamCore(logger, streamContext.ConnectionId, GetStreamType(streamContext));
     }
 }
Example #16
0
 public static void StreamError(ILogger logger, QuicStreamContext streamContext, Exception ex)
 {
     if (logger.IsEnabled(LogLevel.Debug))
     {
         StreamErrorCore(logger, streamContext.ConnectionId, ex);
     }
 }
Example #17
0
 public static void StreamAbortWrite(ILogger logger, QuicStreamContext streamContext, long errorCode, string reason)
 {
     if (logger.IsEnabled(LogLevel.Debug))
     {
         StreamAbortWriteCore(logger, streamContext.ConnectionId, errorCode, reason);
     }
 }
Example #18
0
        public override ValueTask <ConnectionContext> ConnectAsync(IFeatureCollection?features = null, CancellationToken cancellationToken = default)
        {
            QuicStream quicStream;

            var streamDirectionFeature = features?.Get <IStreamDirectionFeature>();

            if (streamDirectionFeature != null)
            {
                if (streamDirectionFeature.CanRead)
                {
                    quicStream = _connection.OpenBidirectionalStream();
                }
                else
                {
                    quicStream = _connection.OpenUnidirectionalStream();
                }
            }
            else
            {
                quicStream = _connection.OpenBidirectionalStream();
            }

            var context = new QuicStreamContext(quicStream, this, _context);

            context.Start();

            _log.ConnectedStream(context);

            return(new ValueTask <ConnectionContext>(context));
        }
Example #19
0
        public ValueTask <ConnectionContext> StartBidirectionalStreamAsync()
        {
            var stream  = _connection.OpenBidirectionalStream();
            var context = new QuicStreamContext(stream, this, _context);

            context.Start();
            return(new ValueTask <ConnectionContext>(context));
        }
Example #20
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);
        }
Example #21
0
        internal bool TryReturnStream(QuicStreamContext stream)
        {
            lock (_poolLock)
            {
                if (!_streamPoolHeartbeatInitialized)
                {
                    // Heartbeat feature is added to connection features by Kestrel.
                    // No event is on the context is raised between feature being added and serving
                    // connections so initialize heartbeat the first time a stream is added to
                    // the connection's stream pool.
                    var heartbeatFeature = Features.Get <IConnectionHeartbeatFeature>();
                    if (heartbeatFeature == null)
                    {
                        throw new InvalidOperationException($"Required {nameof(IConnectionHeartbeatFeature)} not found in connection features.");
                    }

                    heartbeatFeature.OnHeartbeat(static state => ((QuicConnectionContext)state).RemoveExpiredStreams(), this);
Example #22
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();

                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);
        }
Example #23
0
 private static StreamType GetStreamType(QuicStreamContext streamContext) =>
 streamContext.CanRead && streamContext.CanWrite
         ? StreamType.Bidirectional
         : StreamType.Unidirectional;