private static async Task <ArraySegment <byte> > PeekAsyncAwaited(this SocketInput input)
        {
            while (true)
            {
                await input;

                var fin = input.CheckFinOrThrow();

                var begin   = input.ConsumingStart();
                var segment = begin.PeekArraySegment();
                input.ConsumingComplete(begin, begin);

                if (segment.Count != 0 || fin)
                {
                    return(segment);
                }
            }
        }
        public static ValueTask <ArraySegment <byte> > PeekAsync(this SocketInput input)
        {
            while (input.IsCompleted)
            {
                var fin = input.CheckFinOrThrow();

                var begin   = input.ConsumingStart();
                var segment = begin.PeekArraySegment();
                input.ConsumingComplete(begin, begin);

                if (segment.Count != 0 || fin)
                {
                    return(new ValueTask <ArraySegment <byte> >(segment));
                }
            }

            return(new ValueTask <ArraySegment <byte> >(input.PeekAsyncAwaited()));
        }
        private static async Task <int> ReadAsyncAwaited(this SocketInput input, byte[] buffer, int offset, int count)
        {
            while (true)
            {
                await input;

                var fin = input.CheckFinOrThrow();

                var begin = input.ConsumingStart();
                int actual;
                var end = begin.CopyTo(buffer, offset, count, out actual);
                input.ConsumingComplete(end, end);

                if (actual != 0 || fin)
                {
                    return(actual);
                }
            }
        }
        public static ValueTask <int> ReadAsync(this SocketInput input, byte[] buffer, int offset, int count)
        {
            while (input.IsCompleted)
            {
                var fin = input.CheckFinOrThrow();

                var begin = input.ConsumingStart();
                int actual;
                var end = begin.CopyTo(buffer, offset, count, out actual);
                input.ConsumingComplete(end, end);

                if (actual != 0 || fin)
                {
                    return(new ValueTask <int>(actual));
                }
            }

            return(new ValueTask <int>(input.ReadAsyncAwaited(buffer, offset, count)));
        }
예제 #5
0
            private async Task <ArraySegment <byte> > PeekStateMachineAsync()
            {
                while (_mode < Mode.Trailer)
                {
                    while (_mode == Mode.Prefix)
                    {
                        var fin = _input.CheckFinOrThrow();

                        ParseChunkedPrefix();

                        if (_mode != Mode.Prefix)
                        {
                            break;
                        }
                        else if (fin)
                        {
                            _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
                        }

                        await _input;
                    }

                    while (_mode == Mode.Extension)
                    {
                        var fin = _input.CheckFinOrThrow();

                        ParseExtension();

                        if (_mode != Mode.Extension)
                        {
                            break;
                        }
                        else if (fin)
                        {
                            _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
                        }

                        await _input;
                    }

                    while (_mode == Mode.Data)
                    {
                        var fin = _input.CheckFinOrThrow();

                        var segment = PeekChunkedData();

                        if (segment.Count != 0)
                        {
                            return(segment);
                        }
                        else if (_mode != Mode.Data)
                        {
                            break;
                        }
                        else if (fin)
                        {
                            _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
                        }

                        await _input;
                    }

                    while (_mode == Mode.Suffix)
                    {
                        var fin = _input.CheckFinOrThrow();

                        ParseChunkedSuffix();

                        if (_mode != Mode.Suffix)
                        {
                            break;
                        }
                        else if (fin)
                        {
                            _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
                        }

                        await _input;
                    }
                }

                // Chunks finished, parse trailers
                while (_mode == Mode.Trailer)
                {
                    var fin = _input.CheckFinOrThrow();

                    ParseChunkedTrailer();

                    if (_mode != Mode.Trailer)
                    {
                        break;
                    }
                    else if (fin)
                    {
                        _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
                    }

                    await _input;
                }

                if (_mode == Mode.TrailerHeaders)
                {
                    while (!_context.TakeMessageHeaders(_input, _requestHeaders))
                    {
                        if (_input.CheckFinOrThrow())
                        {
                            if (_context.TakeMessageHeaders(_input, _requestHeaders))
                            {
                                break;
                            }
                            else
                            {
                                _context.RejectRequest(RequestRejectionReason.ChunkedRequestIncomplete);
                            }
                        }

                        await _input;
                    }

                    _mode = Mode.Complete;
                }

                return(default(ArraySegment <byte>));
            }
예제 #6
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 RequestProcessingAsync()
        {
            var requestLineStatus = RequestLineStatus.Empty;

            try
            {
                while (!_requestProcessingStopping)
                {
                    ConnectionControl.SetTimeout(_keepAliveMilliseconds, TimeoutAction.CloseConnection);

                    while (!_requestProcessingStopping)
                    {
                        requestLineStatus = TakeStartLine(SocketInput);

                        if (requestLineStatus == RequestLineStatus.Done)
                        {
                            break;
                        }

                        if (SocketInput.CheckFinOrThrow())
                        {
                            // We need to attempt to consume start lines and headers even after
                            // SocketInput.RemoteIntakeFin is set to true to ensure we don't close a
                            // connection without giving the application a chance to respond to a request
                            // sent immediately before the a FIN from the client.
                            requestLineStatus = TakeStartLine(SocketInput);

                            if (requestLineStatus == RequestLineStatus.Empty)
                            {
                                return;
                            }

                            if (requestLineStatus != RequestLineStatus.Done)
                            {
                                RejectRequest(RequestRejectionReason.InvalidRequestLine, requestLineStatus.ToString());
                            }

                            break;
                        }

                        await SocketInput;
                    }

                    InitializeHeaders();

                    while (!_requestProcessingStopping && !TakeMessageHeaders(SocketInput, FrameRequestHeaders))
                    {
                        if (SocketInput.CheckFinOrThrow())
                        {
                            // We need to attempt to consume start lines and headers even after
                            // SocketInput.RemoteIntakeFin is set to true to ensure we don't close a
                            // connection without giving the application a chance to respond to a request
                            // sent immediately before the a FIN from the client.
                            if (!TakeMessageHeaders(SocketInput, FrameRequestHeaders))
                            {
                                RejectRequest(RequestRejectionReason.MalformedRequestInvalidHeaders);
                            }

                            break;
                        }

                        await SocketInput;
                    }

                    if (!_requestProcessingStopping)
                    {
                        var messageBody = MessageBody.For(_httpVersion, FrameRequestHeaders, this);
                        _keepAlive = messageBody.RequestKeepAlive;
                        _upgrade   = messageBody.RequestUpgrade;

                        InitializeStreams(messageBody);

                        var context = _application.CreateContext(this);
                        try
                        {
                            try
                            {
                                await _application.ProcessRequestAsync(context).ConfigureAwait(false);

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

                                if (ex is BadHttpRequestException)
                                {
                                    throw;
                                }
                            }
                            finally
                            {
                                // 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)
                            {
                                ResumeStreams();

                                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();
                                }

                                if (_keepAlive)
                                {
                                    // Finish reading the request body in case the app did not.
                                    await messageBody.Consume();
                                }

                                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 (!_keepAlive)
                    {
                        // End the connection for non keep alive as data incoming may have been thrown off
                        return;
                    }

                    // Don't reset frame state if we're exiting the loop. This avoids losing request rejection
                    // information (for 4xx response), and prevents ObjectDisposedException on HTTPS (ODEs
                    // will be thrown if PrepareRequest is not null and references objects disposed on connection
                    // close - see https://github.com/aspnet/KestrelHttpServer/issues/1103#issuecomment-250237677).
                    if (!_requestProcessingStopping)
                    {
                        Reset();
                    }
                }
            }
            catch (BadHttpRequestException ex)
            {
                // Handle BadHttpRequestException thrown during request line or header parsing.
                // SetBadRequestState logs the error.
                SetBadRequestState(ex);
            }
            catch (IOException ex) when(ex.InnerException is UvException)
            {
                // Don't log ECONNRESET errors made between requests. Browsers like IE will reset connections regularly.
                if (requestLineStatus != RequestLineStatus.Empty ||
                    ((UvException)ex.InnerException).StatusCode != Constants.ECONNRESET)
                {
                    Log.RequestProcessingError(ConnectionId, ex);
                }
            }
            catch (Exception ex)
            {
                Log.LogWarning(0, ex, "Connection processing ended abnormally");
            }
            finally
            {
                try
                {
                    // If _requestAborted is set, the connection has already been closed.
                    if (Volatile.Read(ref _requestAborted) == 0)
                    {
                        await TryProduceInvalidRequestResponse();

                        ConnectionControl.End(ProduceEndType.SocketShutdown);
                    }
                }
                catch (Exception ex)
                {
                    Log.LogWarning(0, ex, "Connection shutdown abnormally");
                }
            }
        }