示例#1
0
    public Http3OutputProducer(
        Http3FrameWriter frameWriter,
        MemoryPool <byte> pool,
        Http3Stream stream,
        KestrelTrace log)
    {
        _frameWriter = frameWriter;
        _memoryPool  = pool;
        _stream      = stream;
        _log         = log;

        _pipe = CreateDataPipe(pool);

        _pipeWriter = _pipe.Writer;
        _pipeReader = _pipe.Reader;

        _flusher = new TimingPipeFlusher(timeoutControl: null, log);
        _flusher.Initialize(_pipeWriter);
        _dataWriteProcessingTask = ProcessDataWrites().Preserve();
    }
示例#2
0
 public static MessageBody For(Http3Stream context)
 {
     return(new Http3MessageBody(context));
 }
示例#3
0
 private Http3MessageBody(Http3Stream context)
     : base(context)
 {
     _context = context;
 }
示例#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;
        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);
            }
        }
    }
示例#5
0
 public Http3MessageBody(Http3Stream context)
     : base(context)
 {
     _context        = context;
     ExtendedConnect = _context.IsExtendedConnectRequest;
 }