Exemplo n.º 1
0
            public void OnStreamCreated(IHttp3Stream stream)
            {
                _inner.OnStreamCreated(stream);

                if (_http3TestBase._runningStreams.TryGetValue(stream.StreamId, out var testStream))
                {
                    testStream.OnStreamCreatedTcs.TrySetResult();
                }
            }
Exemplo n.º 2
0
        public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> application) where TContext : notnull
        {
            // An endpoint MAY avoid creating an encoder stream if it's not going to
            // be used(for example if its encoder doesn't wish to use the dynamic
            // table, or if the maximum size of the dynamic table permitted by the
            // peer is zero).

            // An endpoint MAY avoid creating a decoder stream if its decoder sets
            // the maximum capacity of the dynamic table to zero.

            // Don't create Encoder and Decoder as they aren't used now.

            Exception?         error = null;
            Http3ControlStream?outboundControlStream     = null;
            ValueTask          outboundControlStreamTask = default;
            bool clientAbort = false;

            try
            {
                outboundControlStream = await CreateNewUnidirectionalStreamAsync(application);

                lock (_sync)
                {
                    OutboundControlStream = outboundControlStream;
                }

                // Don't delay on waiting to send outbound control stream settings.
                outboundControlStreamTask = ProcessOutboundControlStreamAsync(outboundControlStream);

                while (_stoppedAcceptingStreams == 0)
                {
                    var streamContext = await _multiplexedContext.AcceptAsync(_acceptStreamsCts.Token);

                    try
                    {
                        // Return null on server close or cancellation.
                        if (streamContext == null)
                        {
                            if (_acceptStreamsCts.Token.IsCancellationRequested)
                            {
                                _acceptStreamsCts = new CancellationTokenSource();
                            }

                            // There is no stream so continue to skip to UpdateConnectionState in finally.
                            // UpdateConnectionState is responsible for updating connection to
                            // stop accepting streams and break out of accept loop.
                            continue;
                        }

                        var streamDirectionFeature = streamContext.Features.Get <IStreamDirectionFeature>();
                        var streamIdFeature        = streamContext.Features.Get <IStreamIdFeature>();

                        Debug.Assert(streamDirectionFeature != null);
                        Debug.Assert(streamIdFeature != null);

                        if (!streamDirectionFeature.CanWrite)
                        {
                            // Unidirectional stream
                            var stream = new Http3ControlStream <TContext>(application, CreateHttpStreamContext(streamContext));
                            _streamLifetimeHandler.OnStreamCreated(stream);

                            ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false);
                        }
                        else
                        {
                            // Request stream

                            // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-5.2-2
                            if (_gracefulCloseStarted)
                            {
                                // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.2-3
                                streamContext.Features.Get <IProtocolErrorCodeFeature>() !.Error = (long)Http3ErrorCode.RequestRejected;
                                streamContext.Abort(new ConnectionAbortedException("HTTP/3 connection is closing and no longer accepts new requests."));
                                await streamContext.DisposeAsync();

                                continue;
                            }

                            // Request stream IDs are tracked.
                            UpdateHighestOpenedRequestStreamId(streamIdFeature.StreamId);

                            var persistentStateFeature = streamContext.Features.Get <IPersistentStateFeature>();
                            Debug.Assert(persistentStateFeature != null, $"Required {nameof(IPersistentStateFeature)} not on stream context.");

                            Http3Stream <TContext> stream;

                            // Check whether there is an existing HTTP/3 stream on the transport stream.
                            // A stream will only be cached if the transport stream itself is reused.
                            if (!persistentStateFeature.State.TryGetValue(StreamPersistentStateKey, out var s))
                            {
                                stream = new Http3Stream <TContext>(application, CreateHttpStreamContext(streamContext));
                                persistentStateFeature.State.Add(StreamPersistentStateKey, stream);
                            }
                            else
                            {
                                stream = (Http3Stream <TContext>)s !;
                                stream.InitializeWithExistingContext(streamContext.Transport);
                            }

                            _streamLifetimeHandler.OnStreamCreated(stream);

                            KestrelEventSource.Log.RequestQueuedStart(stream, AspNetCore.Http.HttpProtocol.Http3);
                            ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false);
                        }
                    }
                    finally
                    {
                        UpdateConnectionState();
                    }
                }
            }
            catch (ConnectionResetException ex)
            {
                lock (_streams)
                {
                    if (_activeRequestCount > 0)
                    {
                        Log.RequestProcessingError(_context.ConnectionId, ex);
                    }
                }
                error       = ex;
                clientAbort = true;
            }
            catch (IOException ex)
            {
                Log.RequestProcessingError(_context.ConnectionId, ex);
                error = ex;
            }
            catch (ConnectionAbortedException ex)
            {
                Log.RequestProcessingError(_context.ConnectionId, ex);
                error = ex;
            }
            catch (Http3ConnectionErrorException ex)
            {
                Log.Http3ConnectionError(_context.ConnectionId, ex);
                error = ex;
            }
            catch (Exception ex)
            {
                error = ex;
            }
            finally
            {
                try
                {
                    // Don't try to send GOAWAY if the client has already closed the connection.
                    if (!clientAbort)
                    {
                        if (TryStopAcceptingStreams() || _gracefulCloseStarted)
                        {
                            await SendGoAwayAsync(GetCurrentGoAwayStreamId());
                        }
                    }

                    // Abort active request streams.
                    lock (_streams)
                    {
                        foreach (var stream in _streams.Values)
                        {
                            stream.Abort(CreateConnectionAbortError(error, clientAbort), (Http3ErrorCode)_errorCodeFeature.Error);
                        }
                    }

                    if (outboundControlStream != null)
                    {
                        // Don't gracefully close the outbound control stream. If the peer detects
                        // the control stream closes it will close with a procotol error.
                        // Instead, allow control stream to be automatically aborted when the
                        // connection is aborted.
                        await outboundControlStreamTask;
                    }

                    // Complete
                    Abort(CreateConnectionAbortError(error, clientAbort), (Http3ErrorCode)_errorCodeFeature.Error);

                    // Wait for active requests to complete.
                    while (_activeRequestCount > 0)
                    {
                        await _streamCompletionAwaitable;
                    }

                    _context.TimeoutControl.CancelTimeout();
                }
                catch
                {
                    Abort(CreateConnectionAbortError(error, clientAbort), Http3ErrorCode.InternalError);
                    throw;
                }
                finally
                {
                    // Connection can close without processing any request streams.
                    var streamId = _highestOpenedRequestStreamId != DefaultHighestOpenedRequestStreamId
                        ? _highestOpenedRequestStreamId
                        : (long?)null;

                    Log.Http3ConnectionClosed(_context.ConnectionId, streamId);
                }
            }
        }
Exemplo n.º 3
0
        public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> application) where TContext : notnull
        {
            // An endpoint MAY avoid creating an encoder stream if it's not going to
            // be used(for example if its encoder doesn't wish to use the dynamic
            // table, or if the maximum size of the dynamic table permitted by the
            // peer is zero).

            // An endpoint MAY avoid creating a decoder stream if its decoder sets
            // the maximum capacity of the dynamic table to zero.

            // Don't create Encoder and Decoder as they aren't used now.

            Exception?error = null;
            ValueTask outboundControlStreamTask = default;

            try
            {
                var outboundControlStream = await CreateNewUnidirectionalStreamAsync(application);

                lock (_sync)
                {
                    OutboundControlStream = outboundControlStream;
                }

                // Don't delay on waiting to send outbound control stream settings.
                outboundControlStreamTask = ProcessOutboundControlStreamAsync(outboundControlStream);

                while (_isClosed == 0)
                {
                    // Don't pass a cancellation token to AcceptAsync.
                    // AcceptAsync will return null if the connection is gracefully shutting down or aborted.
                    var streamContext = await _multiplexedContext.AcceptAsync();

                    try
                    {
                        if (streamContext == null)
                        {
                            break;
                        }

                        var streamDirectionFeature = streamContext.Features.Get <IStreamDirectionFeature>();
                        var streamIdFeature        = streamContext.Features.Get <IStreamIdFeature>();

                        Debug.Assert(streamDirectionFeature != null);
                        Debug.Assert(streamIdFeature != null);

                        if (!streamDirectionFeature.CanWrite)
                        {
                            // Unidirectional stream
                            var stream = new Http3ControlStream <TContext>(application, CreateHttpStreamContext(streamContext));
                            _streamLifetimeHandler.OnStreamCreated(stream);

                            ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false);
                        }
                        else
                        {
                            var persistentStateFeature = streamContext.Features.Get <IPersistentStateFeature>();
                            Debug.Assert(persistentStateFeature != null, $"Required {nameof(IPersistentStateFeature)} not on stream context.");

                            // Request stream
                            UpdateHighestStreamId(streamIdFeature.StreamId);

                            Http3Stream <TContext> stream;

                            // Check whether there is an existing HTTP/3 stream on the transport stream.
                            // A stream will only be cached if the transport stream itself is reused.
                            if (!persistentStateFeature.State.TryGetValue(StreamPersistentStateKey, out var s))
                            {
                                stream = new Http3Stream <TContext>(application, CreateHttpStreamContext(streamContext));
                                persistentStateFeature.State.Add(StreamPersistentStateKey, stream);
                            }
                            else
                            {
                                stream = (Http3Stream <TContext>)s !;
                                stream.InitializeWithExistingContext(streamContext.Transport);
                            }

                            _streamLifetimeHandler.OnStreamCreated(stream);

                            KestrelEventSource.Log.RequestQueuedStart(stream, AspNetCore.Http.HttpProtocol.Http3);
                            ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false);
                        }
                    }
                    finally
                    {
                        UpdateConnectionState();
                    }
                }
            }
            catch (ConnectionResetException ex)
            {
                lock (_streams)
                {
                    if (_activeRequestCount > 0)
                    {
                        Log.RequestProcessingError(_context.ConnectionId, ex);
                    }
                }
                error = ex;
            }
            catch (IOException ex)
            {
                Log.RequestProcessingError(_context.ConnectionId, ex);
                error = ex;
            }
            catch (ConnectionAbortedException ex)
            {
                Log.RequestProcessingError(_context.ConnectionId, ex);
                error = ex;
            }
            catch (Http3ConnectionErrorException ex)
            {
                Log.Http3ConnectionError(_context.ConnectionId, ex);
                error = ex;
            }
            catch (Exception ex)
            {
                error = ex;
            }
            finally
            {
                var connectionError = error as ConnectionAbortedException
                                      ?? new ConnectionAbortedException(CoreStrings.Http3ConnectionFaulted, error !);

                try
                {
                    if (TryClose())
                    {
                        // This throws when connection is shut down.
                        // TODO how to make it so we can distinguish between Abort from server vs client?
                        await SendGoAway(GetHighestStreamId());
                    }

                    foreach (var stream in _streams.Values)
                    {
                        stream.Abort(connectionError, (Http3ErrorCode)_errorCodeFeature.Error);
                    }

                    lock (_sync)
                    {
                        OutboundControlStream?.Abort(connectionError, (Http3ErrorCode)_errorCodeFeature.Error);
                    }

                    while (_activeRequestCount > 0)
                    {
                        await _streamCompletionAwaitable;
                    }

                    await outboundControlStreamTask;

                    _context.TimeoutControl.CancelTimeout();
                    _context.TimeoutControl.StartDrainTimeout(Limits.MinResponseDataRate, Limits.MaxResponseBufferSize);
                }
                catch
                {
                    Abort(connectionError, Http3ErrorCode.InternalError);
                    throw;
                }
            }
        }
Exemplo n.º 4
0
        public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> application) where TContext : notnull
        {
            // An endpoint MAY avoid creating an encoder stream if it's not going to
            // be used(for example if its encoder doesn't wish to use the dynamic
            // table, or if the maximum size of the dynamic table permitted by the
            // peer is zero).

            // An endpoint MAY avoid creating a decoder stream if its decoder sets
            // the maximum capacity of the dynamic table to zero.

            // Don't create Encoder and Decoder as they aren't used now.
            Exception?error = null;

            // TODO should we await the control stream task?
            var controlTask = CreateControlStream(application);

            try
            {
                while (_isClosed == 0)
                {
                    // TODO implement way to unblock loop for one call to accept async to update state.
                    // Use cts for now, update to custom awaitable or different solution in the future.
                    var streamContext = await _multiplexedContext.AcceptAsync();

                    try
                    {
                        if (streamContext == null)
                        {
                            break;
                        }

                        var quicStreamFeature = streamContext.Features.Get <IStreamDirectionFeature>();
                        var streamIdFeature   = streamContext.Features.Get <IStreamIdFeature>();

                        Debug.Assert(quicStreamFeature != null);
                        Debug.Assert(streamIdFeature != null);

                        var httpConnectionContext = new Http3StreamContext(
                            streamContext.ConnectionId,
                            protocols: default,
                            connectionContext: null !, // TODO connection context is null here. Should we set it to anything?
                            _context.ServiceContext,
                            streamContext.Features,
                            _context.MemoryPool,
                            streamContext.LocalEndPoint as IPEndPoint,
                            streamContext.RemoteEndPoint as IPEndPoint,
                            streamContext.Transport,
                            _streamLifetimeHandler,
                            streamContext,
                            _serverSettings);
                        httpConnectionContext.TimeoutControl = _context.TimeoutControl;

                        var streamId = streamIdFeature.StreamId;

                        if (!quicStreamFeature.CanWrite)
                        {
                            // Unidirectional stream
                            var stream = new Http3ControlStream <TContext>(application, httpConnectionContext);
                            _streamLifetimeHandler.OnStreamCreated(stream);

                            ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false);
                        }
                        else
                        {
                            // Request stream
                            UpdateHighestStreamId(streamId);

                            var stream = new Http3Stream <TContext>(application, httpConnectionContext);
                            _streamLifetimeHandler.OnStreamCreated(stream);

                            KestrelEventSource.Log.RequestQueuedStart(stream, AspNetCore.Http.HttpProtocol.Http3);
                            ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false);
                        }
                    }
Exemplo n.º 5
0
        public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> application) where TContext : notnull
        {
            // An endpoint MAY avoid creating an encoder stream if it's not going to
            // be used(for example if its encoder doesn't wish to use the dynamic
            // table, or if the maximum size of the dynamic table permitted by the
            // peer is zero).

            // An endpoint MAY avoid creating a decoder stream if its decoder sets
            // the maximum capacity of the dynamic table to zero.

            // Don't create Encoder and Decoder as they aren't used now.

            Exception?error = null;
            ValueTask outboundControlStreamTask = default;

            try
            {
                var outboundControlStream = await CreateNewUnidirectionalStreamAsync(application);

                lock (_sync)
                {
                    OutboundControlStream = outboundControlStream;
                }

                // Don't delay on waiting to send outbound control stream settings.
                outboundControlStreamTask = ProcessOutboundControlStreamAsync(outboundControlStream);

                while (_isClosed == 0)
                {
                    // Don't pass a cancellation token to AcceptAsync.
                    // AcceptAsync will return null if the connection is gracefully shutting down or aborted.
                    var streamContext = await _multiplexedContext.AcceptAsync();

                    try
                    {
                        if (streamContext == null)
                        {
                            break;
                        }

                        var streamDirectionFeature = streamContext.Features.Get <IStreamDirectionFeature>();
                        var streamIdFeature        = streamContext.Features.Get <IStreamIdFeature>();

                        Debug.Assert(streamDirectionFeature != null);
                        Debug.Assert(streamIdFeature != null);

                        var httpConnectionContext = new Http3StreamContext(
                            streamContext.ConnectionId,
                            protocols: default,
                            connectionContext: null !, // TODO connection context is null here. Should we set it to anything?
                            _context.ServiceContext,
                            streamContext.Features,
                            _context.MemoryPool,
                            streamContext.LocalEndPoint as IPEndPoint,
                            streamContext.RemoteEndPoint as IPEndPoint,
                            streamContext.Transport,
                            _streamLifetimeHandler,
                            streamContext,
                            _clientSettings,
                            _serverSettings);
                        httpConnectionContext.TimeoutControl = _context.TimeoutControl;

                        if (!streamDirectionFeature.CanWrite)
                        {
                            // Unidirectional stream
                            var stream = new Http3ControlStream <TContext>(application, httpConnectionContext);
                            _streamLifetimeHandler.OnStreamCreated(stream);

                            ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false);
                        }
                        else
                        {
                            // Request stream
                            UpdateHighestStreamId(streamIdFeature.StreamId);

                            var stream = new Http3Stream <TContext>(application, httpConnectionContext);
                            _streamLifetimeHandler.OnStreamCreated(stream);

                            KestrelEventSource.Log.RequestQueuedStart(stream, AspNetCore.Http.HttpProtocol.Http3);
                            ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false);
                        }
                    }