Ejemplo n.º 1
0
        public void Initialize(Http2StreamContext context)
        {
            base.Initialize(context);

            CanReuse             = false;
            _decrementCalled     = false;
            _completionState     = StreamCompletionFlags.None;
            InputRemaining       = null;
            RequestBodyStarted   = false;
            DrainExpirationTicks = 0;

            _context = context;

            // First time the stream is used we need to create flow control, producer and pipes.
            // When a stream is reused these types will be reset and reused.
            if (_inputFlowControl == null)
            {
                _inputFlowControl = new StreamInputFlowControl(
                    this,
                    context.FrameWriter,
                    context.ConnectionInputFlowControl,
                    context.ServerPeerSettings.InitialWindowSize,
                    context.ServerPeerSettings.InitialWindowSize / 2);

                _outputFlowControl = new StreamOutputFlowControl(
                    context.ConnectionOutputFlowControl,
                    context.ClientPeerSettings.InitialWindowSize);

                _http2Output = new Http2OutputProducer(this, context, _outputFlowControl);

                RequestBodyPipe = CreateRequestBodyPipe();

                Output = _http2Output;
            }
            else
            {
                _inputFlowControl.Reset();
                _outputFlowControl.Reset(context.ClientPeerSettings.InitialWindowSize);
                _http2Output.StreamReset();
                RequestBodyPipe.Reset();
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Primary loop which consumes socket input, parses it for protocol framing, and invokes the
        /// application delegate for as long as the socket is intended to remain open.
        /// The resulting Task from this loop is preserved in a field which is used when the server needs
        /// to drain and close all currently active connections.
        /// </summary>
        public override async Task ProcessRequestsAsync()
        {
            try
            {
                while (!_requestProcessingStopping)
                {
                    TimeoutControl.SetTimeout(_keepAliveTicks, TimeoutAction.CloseConnection);

                    Reset();

                    while (!_requestProcessingStopping)
                    {
                        var result = await Input.ReadAsync();

                        var examined = result.Buffer.End;
                        var consumed = result.Buffer.End;

                        try
                        {
                            ParseRequest(result.Buffer, out consumed, out examined);
                        }
                        catch (InvalidOperationException)
                        {
                            if (_requestProcessingStatus == RequestProcessingStatus.ParsingHeaders)
                            {
                                throw BadHttpRequestException.GetException(RequestRejectionReason
                                                                           .MalformedRequestInvalidHeaders);
                            }
                            throw;
                        }
                        finally
                        {
                            Input.Advance(consumed, examined);
                        }

                        if (_requestProcessingStatus == RequestProcessingStatus.AppStarted)
                        {
                            break;
                        }

                        if (result.IsCompleted)
                        {
                            switch (_requestProcessingStatus)
                            {
                            case RequestProcessingStatus.RequestPending:
                                return;

                            case RequestProcessingStatus.ParsingRequestLine:
                                throw BadHttpRequestException.GetException(
                                          RequestRejectionReason.InvalidRequestLine);

                            case RequestProcessingStatus.ParsingHeaders:
                                throw BadHttpRequestException.GetException(
                                          RequestRejectionReason.MalformedRequestInvalidHeaders);
                            }
                        }
                    }

                    if (!_requestProcessingStopping)
                    {
                        EnsureHostHeaderExists();

                        var messageBody = MessageBody.For(_httpVersion, FrameRequestHeaders, this);
                        _keepAlive        = messageBody.RequestKeepAlive;
                        _upgradeAvailable = messageBody.RequestUpgrade;

                        InitializeStreams(messageBody);

                        var context = _application.CreateContext(this);
                        try
                        {
                            try
                            {
                                KestrelEventSource.Log.RequestStart(this);

                                await _application.ProcessRequestAsync(context);

                                if (Volatile.Read(ref _requestAborted) == 0)
                                {
                                    VerifyResponseContentLength();
                                }
                            }
                            catch (Exception ex)
                            {
                                ReportApplicationError(ex);

                                if (ex is BadHttpRequestException)
                                {
                                    throw;
                                }
                            }
                            finally
                            {
                                KestrelEventSource.Log.RequestStop(this);

                                // Trigger OnStarting if it hasn't been called yet and the app hasn't
                                // already failed. If an OnStarting callback throws we can go through
                                // our normal error handling in ProduceEnd.
                                // https://github.com/aspnet/KestrelHttpServer/issues/43
                                if (!HasResponseStarted && _applicationException == null && _onStarting != null)
                                {
                                    await FireOnStarting();
                                }

                                PauseStreams();

                                if (_onCompleted != null)
                                {
                                    await FireOnCompleted();
                                }
                            }

                            // If _requestAbort is set, the connection has already been closed.
                            if (Volatile.Read(ref _requestAborted) == 0)
                            {
                                if (HasResponseStarted)
                                {
                                    // If the response has already started, call ProduceEnd() before
                                    // consuming the rest of the request body to prevent
                                    // delaying clients waiting for the chunk terminator:
                                    //
                                    // https://github.com/dotnet/corefx/issues/17330#issuecomment-288248663
                                    //
                                    // ProduceEnd() must be called before _application.DisposeContext(), to ensure
                                    // HttpContext.Response.StatusCode is correctly set when
                                    // IHttpContextFactory.Dispose(HttpContext) is called.
                                    await ProduceEnd();
                                }

                                // ForZeroContentLength does not complete the reader nor the writer
                                if (!messageBody.IsEmpty && _keepAlive)
                                {
                                    // Finish reading the request body in case the app did not.
                                    TimeoutControl.SetTimeout(Constants.RequestBodyDrainTimeout.Ticks, TimeoutAction.SendTimeoutResponse);
                                    await messageBody.ConsumeAsync();

                                    TimeoutControl.CancelTimeout();
                                }

                                if (!HasResponseStarted)
                                {
                                    await ProduceEnd();
                                }
                            }
                            else if (!HasResponseStarted)
                            {
                                // If the request was aborted and no response was sent, there's no
                                // meaningful status code to log.
                                StatusCode = 0;
                            }
                        }
                        catch (BadHttpRequestException ex)
                        {
                            // Handle BadHttpRequestException thrown during app execution or remaining message body consumption.
                            // This has to be caught here so StatusCode is set properly before disposing the HttpContext
                            // (DisposeContext logs StatusCode).
                            SetBadRequestState(ex);
                        }
                        finally
                        {
                            _application.DisposeContext(context, _applicationException);

                            // StopStreams should be called before the end of the "if (!_requestProcessingStopping)" block
                            // to ensure InitializeStreams has been called.
                            StopStreams();

                            if (HasStartedConsumingRequestBody)
                            {
                                RequestBodyPipe.Reader.Complete();

                                // Wait for MessageBody.PumpAsync() to call RequestBodyPipe.Writer.Complete().
                                await messageBody.StopAsync();

                                // At this point both the request body pipe reader and writer should be completed.
                                RequestBodyPipe.Reset();
                            }
                        }
                    }

                    if (!_keepAlive)
                    {
                        // End the connection for non keep alive as data incoming may have been thrown off
                        return;
                    }
                }
            }
            catch (BadHttpRequestException ex)
            {
                // Handle BadHttpRequestException thrown during request line or header parsing.
                // SetBadRequestState logs the error.
                SetBadRequestState(ex);
            }
            catch (ConnectionResetException ex)
            {
                // Don't log ECONNRESET errors made between requests. Browsers like IE will reset connections regularly.
                if (_requestProcessingStatus != RequestProcessingStatus.RequestPending)
                {
                    Log.RequestProcessingError(ConnectionId, ex);
                }
            }
            catch (IOException ex)
            {
                Log.RequestProcessingError(ConnectionId, ex);
            }
            catch (Exception ex)
            {
                Log.LogWarning(0, ex, CoreStrings.RequestProcessingEndError);
            }
            finally
            {
                try
                {
                    Input.Complete();

                    // If _requestAborted is set, the connection has already been closed.
                    if (Volatile.Read(ref _requestAborted) == 0)
                    {
                        await TryProduceInvalidRequestResponse();

                        Output.Dispose();
                    }
                }
                catch (Exception ex)
                {
                    Log.LogWarning(0, ex, CoreStrings.ConnectionShutdownError);
                }
            }
        }