コード例 #1
0
 protected override void OnReadStarting()
 {
     if (_contentLength > _context.MaxRequestBodySize)
     {
         KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge);
     }
 }
コード例 #2
0
        public bool TakeStartLine(ref SequenceReader <byte> reader)
        {
            // Make sure the buffer is limited
            if (reader.Remaining >= ServerOptions.Limits.MaxRequestLineSize)
            {
                // Input oversize, cap amount checked
                return(TrimAndTakeStartLine(ref reader));
            }

            return(_parser.ParseRequestLine(new Http1ParsingHandler(this), ref reader));

            bool TrimAndTakeStartLine(ref SequenceReader <byte> reader)
            {
                var trimmedBuffer = reader.Sequence.Slice(reader.Position, ServerOptions.Limits.MaxRequestLineSize);
                var trimmedReader = new SequenceReader <byte>(trimmedBuffer);

                if (!_parser.ParseRequestLine(new Http1ParsingHandler(this), ref trimmedReader))
                {
                    // We read the maximum allowed but didn't complete the start line.
                    KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestLineTooLong);
                }

                reader.Advance(trimmedReader.Consumed);
                return(true);
            }
        }
コード例 #3
0
 protected override void OnReadStarting()
 {
     // Note ContentLength or MaxRequestBodySize may be null
     if (_context.RequestHeaders.ContentLength > _context.MaxRequestBodySize)
     {
         KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge);
     }
 }
コード例 #4
0
        protected override void OnReadStarting()
        {
            var maxRequestBodySize = _context.MaxRequestBodySize;

            if (_contentLength > maxRequestBodySize)
            {
                KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge, maxRequestBodySize.GetValueOrDefault().ToString(CultureInfo.InvariantCulture));
            }
        }
コード例 #5
0
ファイル: MessageBody.cs プロジェクト: zx972243884/aspnetcore
        protected void AddAndCheckObservedBytes(long observedBytes)
        {
            _observedBytes += observedBytes;

            if (_observedBytes > _context.MaxRequestBodySize)
            {
                KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge);
            }
        }
コード例 #6
0
        private static long ParseContentLength(string value)
        {
            if (!HeaderUtilities.TryParseNonNegativeInt64(value, out var parsed))
            {
                KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidContentLength, value);
            }

            return(parsed);
        }
コード例 #7
0
        protected void ThrowUnexpectedEndOfRequestContent()
        {
            // OnInputOrOutputCompleted() is an idempotent method that closes the connection. Sometimes
            // input completion is observed here before the Input.OnWriterCompleted() callback is fired,
            // so we call OnInputOrOutputCompleted() now to prevent a race in our tests where a 400
            // response is written after observing the unexpected end of request content instead of just
            // closing the connection without a response as expected.
            _context.OnInputOrOutputCompleted();

            KestrelBadHttpRequestException.Throw(RequestRejectionReason.UnexpectedEndOfRequestContent);
        }
コード例 #8
0
 private static unsafe void GetHeaderName(Span <char> buffer, IntPtr state)
 {
     fixed(char *output = &MemoryMarshal.GetReference(buffer))
     {
         // This version of AsciiUtilities returns null if there are any null (0 byte) characters
         // in the string
         if (!StringUtilities.TryGetAsciiString((byte *)state.ToPointer(), output, buffer.Length))
         {
             KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidCharactersInHeaderName);
         }
     }
 }
コード例 #9
0
        public bool TakeMessageHeaders(ref SequenceReader <byte> reader, bool trailers)
        {
            // Make sure the buffer is limited
            if (reader.Remaining > _remainingRequestHeadersBytesAllowed)
            {
                // Input oversize, cap amount checked
                return(TrimAndTakeMessageHeaders(ref reader, trailers));
            }

            var alreadyConsumed = reader.Consumed;

            try
            {
                var result = _parser.ParseHeaders(new Http1ParsingHandler(this, trailers), ref reader);
                if (result)
                {
                    TimeoutControl.CancelTimeout();
                }

                return(result);
            }
            finally
            {
                _remainingRequestHeadersBytesAllowed -= reader.Consumed - alreadyConsumed;
            }

            bool TrimAndTakeMessageHeaders(ref SequenceReader <byte> reader, bool trailers)
            {
                var trimmedBuffer = reader.Sequence.Slice(reader.Position, _remainingRequestHeadersBytesAllowed);
                var trimmedReader = new SequenceReader <byte>(trimmedBuffer);

                try
                {
                    if (!_parser.ParseHeaders(new Http1ParsingHandler(this, trailers), ref trimmedReader))
                    {
                        // We read the maximum allowed but didn't complete the headers.
                        KestrelBadHttpRequestException.Throw(RequestRejectionReason.HeadersExceedMaxTotalSize);
                    }

                    TimeoutControl.CancelTimeout();

                    reader.Advance(trimmedReader.Consumed);

                    return(true);
                }
                finally
                {
                    _remainingRequestHeadersBytesAllowed -= trimmedReader.Consumed;
                }
            }
        }
コード例 #10
0
        private void AppendContentLength(ReadOnlySpan <byte> value)
        {
            if (_contentLength.HasValue)
            {
                KestrelBadHttpRequestException.Throw(RequestRejectionReason.MultipleContentLengths);
            }

            if (!Utf8Parser.TryParse(value, out long parsed, out var consumed) ||
                parsed < 0 ||
                consumed != value.Length)
            {
                KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidContentLength, value.GetRequestHeaderString(HeaderNames.ContentLength, EncodingSelector));
            }

            _contentLength = parsed;
        }
コード例 #11
0
        private void AppendContentLengthCustomEncoding(ReadOnlySpan <byte> value, Encoding?customEncoding)
        {
            if (_contentLength.HasValue)
            {
                KestrelBadHttpRequestException.Throw(RequestRejectionReason.MultipleContentLengths);
            }

            // long.MaxValue = 9223372036854775807 (19 chars)
            Span <char> decodedChars = stackalloc char[20];
            var         numChars     = customEncoding !.GetChars(value, decodedChars);
            long        parsed       = -1;

            if (numChars > 19 ||
                !long.TryParse(decodedChars.Slice(0, numChars), NumberStyles.Integer, CultureInfo.InvariantCulture, out parsed) ||
                parsed < 0)
            {
                KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidContentLength, value.GetRequestHeaderString(HeaderNames.ContentLength, EncodingSelector));
            }

            _contentLength = parsed;
        }
コード例 #12
0
    private void VerifyIsNotReading()
    {
        if (!_isReading)
        {
            return;
        }

        if (_cannotResetInputPipe)
        {
            if (_readResult.IsCompleted)
            {
                KestrelBadHttpRequestException.Throw(RequestRejectionReason.UnexpectedEndOfRequestContent);
            }

            if (_context.RequestTimedOut)
            {
                KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTimeout);
            }
        }

        throw new InvalidOperationException("Reading is already in progress.");
    }
コード例 #13
0
    public override bool TryReadInternal(out ReadResult readResult)
    {
        VerifyIsNotReading();

        if (_readCompleted)
        {
            _isReading = true;
            readResult = new ReadResult(_readResult.Buffer, Interlocked.Exchange(ref _userCanceled, 0) == 1, isCompleted: true);
            return(true);
        }

        if (_context.RequestTimedOut)
        {
            KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTimeout);
        }

        TryStartAsync();

        // The while(true) because we don't want to return a canceled ReadResult if the user themselves didn't cancel it.
        while (true)
        {
            if (!_context.Input.TryRead(out _readResult))
            {
                readResult = default;
                return(false);
            }

            if (!_readResult.IsCanceled || Interlocked.Exchange(ref _userCanceled, 0) == 1 || _cannotResetInputPipe)
            {
                break;
            }

            _context.Input.AdvanceTo(_readResult.Buffer.Start);
        }

        if (_readResult.IsCompleted)
        {
            if (_cannotResetInputPipe)
            {
                _isReading = true;
            }
            else
            {
                _context.Input.AdvanceTo(_readResult.Buffer.Start);
            }

            ThrowUnexpectedEndOfRequestContent();
        }

        var returnedReadResultLength = CreateReadResultFromConnectionReadResult();

        // Don't count bytes belonging to the next request, since read rate timeouts are done on a per-request basis.
        CountBytesRead(returnedReadResultLength);

        // Only set _isReading if we are returning true.
        _isReading = true;
        readResult = _readResult;

        if (readResult.IsCompleted)
        {
            TryStop();
        }

        return(true);
    }
コード例 #14
0
        public static MessageBody For(
            HttpVersion httpVersion,
            HttpRequestHeaders headers,
            Http1Connection context)
        {
            // see also http://tools.ietf.org/html/rfc2616#section-4.4
            var keepAlive = httpVersion != HttpVersion.Http10;
            var upgrade   = false;

            if (headers.HasConnection)
            {
                var connectionOptions = HttpHeaders.ParseConnection(headers.HeaderConnection);

                upgrade   = (connectionOptions & ConnectionOptions.Upgrade) != 0;
                keepAlive = keepAlive || (connectionOptions & ConnectionOptions.KeepAlive) != 0;
                keepAlive = keepAlive && (connectionOptions & ConnectionOptions.Close) == 0;
            }

            // Ignore upgrades if the request has a body. Technically it's possible to support, but we'd have to add a lot
            // more logic to allow reading/draining the normal body before the connection could be fully upgraded.
            // See https://tools.ietf.org/html/rfc7230#section-6.7, https://tools.ietf.org/html/rfc7540#section-3.2
            if (upgrade &&
                headers.ContentLength.GetValueOrDefault() == 0 &&
                headers.HeaderTransferEncoding.Count == 0)
            {
                context.OnTrailersComplete(); // No trailers for these.
                return(new Http1UpgradeMessageBody(context, keepAlive));
            }

            if (headers.HasTransferEncoding)
            {
                var transferEncoding = headers.HeaderTransferEncoding;
                var transferCoding   = HttpHeaders.GetFinalTransferCoding(transferEncoding);

                // https://tools.ietf.org/html/rfc7230#section-3.3.3
                // If a Transfer-Encoding header field
                // is present in a request and the chunked transfer coding is not
                // the final encoding, the message body length cannot be determined
                // reliably; the server MUST respond with the 400 (Bad Request)
                // status code and then close the connection.
                if (transferCoding != TransferCoding.Chunked)
                {
                    KestrelBadHttpRequestException.Throw(RequestRejectionReason.FinalTransferCodingNotChunked, transferEncoding);
                }

                // TODO may push more into the wrapper rather than just calling into the message body
                // NBD for now.
                return(new Http1ChunkedEncodingMessageBody(context, keepAlive));
            }

            if (headers.ContentLength.HasValue)
            {
                var contentLength = headers.ContentLength.Value;

                if (contentLength == 0)
                {
                    return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose);
                }

                return(new Http1ContentLengthMessageBody(context, contentLength, keepAlive));
            }

            // If we got here, request contains no Content-Length or Transfer-Encoding header.
            // Reject with 411 Length Required.
            if (context.Method == HttpMethod.Post || context.Method == HttpMethod.Put)
            {
                var requestRejectionReason = httpVersion == HttpVersion.Http11 ? RequestRejectionReason.LengthRequired : RequestRejectionReason.LengthRequiredHttp10;
                KestrelBadHttpRequestException.Throw(requestRejectionReason, context.Method);
            }

            context.OnTrailersComplete(); // No trailers for these.
            return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose);
        }
コード例 #15
0
    private async Task PumpAsync()
    {
        Debug.Assert(!RequestUpgrade, "Upgraded connections should never use this code path!");

        Exception?error = null;

        try
        {
            var awaitable = _context.Input.ReadAsync();

            if (!awaitable.IsCompleted)
            {
                await TryProduceContinueAsync();
            }

            while (true)
            {
                var result = await awaitable;

                if (_context.RequestTimedOut)
                {
                    KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTimeout);
                }

                var readableBuffer = result.Buffer;
                var consumed       = readableBuffer.Start;
                var examined       = readableBuffer.Start;

                try
                {
                    if (_canceled)
                    {
                        break;
                    }

                    if (!readableBuffer.IsEmpty)
                    {
                        bool done;
                        done = Read(readableBuffer, _requestBodyPipe.Writer, out consumed, out examined);

                        await _requestBodyPipe.Writer.FlushAsync();

                        if (done)
                        {
                            break;
                        }
                    }

                    // Read() will have already have greedily consumed the entire request body if able.
                    if (result.IsCompleted)
                    {
                        ThrowUnexpectedEndOfRequestContent();
                    }
                }
                finally
                {
                    _context.Input.AdvanceTo(consumed, examined);
                }

                awaitable = _context.Input.ReadAsync();
            }
        }
        catch (Exception ex)
        {
            error = ex;
        }
        finally
        {
            await _requestBodyPipe.Writer.CompleteAsync(error);
        }
    }
コード例 #16
0
        public bool ParseHeaders(TRequestHandler handler, ref SequenceReader <byte> reader)
        {
            while (!reader.End)
            {
                var span = reader.UnreadSpan;
                while (span.Length > 0)
                {
                    var ch1       = (byte)0;
                    var ch2       = (byte)0;
                    var readAhead = 0;

                    // Fast path, we're still looking at the same span
                    if (span.Length >= 2)
                    {
                        ch1 = span[0];
                        ch2 = span[1];
                    }
                    else if (reader.TryRead(out ch1)) // Possibly split across spans
                    {
                        // Note if we read ahead by 1 or 2 bytes
                        readAhead = (reader.TryRead(out ch2)) ? 2 : 1;
                    }

                    if (ch1 == ByteCR)
                    {
                        // Check for final CRLF.
                        if (ch2 == ByteLF)
                        {
                            // If we got 2 bytes from the span directly so skip ahead 2 so that
                            // the reader's state matches what we expect
                            if (readAhead == 0)
                            {
                                reader.Advance(2);
                            }

                            // Double CRLF found, so end of headers.
                            handler.OnHeadersComplete(endStream: false);
                            return(true);
                        }
                        else if (readAhead == 1)
                        {
                            // Didn't read 2 bytes, reset the reader so we don't consume anything
                            reader.Rewind(1);
                            return(false);
                        }

                        Debug.Assert(readAhead == 0 || readAhead == 2);
                        // Headers don't end in CRLF line.

                        KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidRequestHeadersNoCRLF);
                    }

                    var length = 0;
                    // We only need to look for the end if we didn't read ahead; otherwise there isn't enough in
                    // in the span to contain a header.
                    if (readAhead == 0)
                    {
                        length = span.IndexOfAny(ByteCR, ByteLF);
                        // If not found length with be -1; casting to uint will turn it to uint.MaxValue
                        // which will be larger than any possible span.Length. This also serves to eliminate
                        // the bounds check for the next lookup of span[length]
                        if ((uint)length < (uint)span.Length)
                        {
                            // Early memory read to hide latency
                            var expectedCR = span[length];
                            // Correctly has a CR, move to next
                            length++;

                            if (expectedCR != ByteCR)
                            {
                                // Sequence needs to be CRLF not LF first.
                                RejectRequestHeader(span[..length]);
コード例 #17
0
 public void HandleRequestHeadersTimeout()
 {
     Log.ConnectionBadRequest(ConnectionId, KestrelBadHttpRequestException.GetException(RequestRejectionReason.RequestHeadersTimeout));
     Abort(new ConnectionAbortedException(CoreStrings.BadRequest_RequestHeadersTimeout), Http3ErrorCode.RequestRejected);
 }
コード例 #18
0
        public static MessageBody For(
            HttpVersion httpVersion,
            HttpRequestHeaders headers,
            Http1Connection context)
        {
            // see also http://tools.ietf.org/html/rfc2616#section-4.4
            var keepAlive = httpVersion != HttpVersion.Http10;

            var upgrade = false;

            if (headers.HasConnection)
            {
                var connectionOptions = HttpHeaders.ParseConnection(headers.HeaderConnection);

                upgrade   = (connectionOptions & ConnectionOptions.Upgrade) != 0;
                keepAlive = (connectionOptions & ConnectionOptions.KeepAlive) != 0;
            }

            if (upgrade)
            {
                if (headers.HeaderTransferEncoding.Count > 0 || (headers.ContentLength.HasValue && headers.ContentLength.Value != 0))
                {
                    KestrelBadHttpRequestException.Throw(RequestRejectionReason.UpgradeRequestCannotHavePayload);
                }

                context.OnTrailersComplete(); // No trailers for these.
                return(new Http1UpgradeMessageBody(context));
            }

            if (headers.HasTransferEncoding)
            {
                var transferEncoding = headers.HeaderTransferEncoding;
                var transferCoding   = HttpHeaders.GetFinalTransferCoding(transferEncoding);

                // https://tools.ietf.org/html/rfc7230#section-3.3.3
                // If a Transfer-Encoding header field
                // is present in a request and the chunked transfer coding is not
                // the final encoding, the message body length cannot be determined
                // reliably; the server MUST respond with the 400 (Bad Request)
                // status code and then close the connection.
                if (transferCoding != TransferCoding.Chunked)
                {
                    KestrelBadHttpRequestException.Throw(RequestRejectionReason.FinalTransferCodingNotChunked, transferEncoding);
                }

                // TODO may push more into the wrapper rather than just calling into the message body
                // NBD for now.
                return(new Http1ChunkedEncodingMessageBody(keepAlive, context));
            }

            if (headers.ContentLength.HasValue)
            {
                var contentLength = headers.ContentLength.Value;

                if (contentLength == 0)
                {
                    return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose);
                }

                return(new Http1ContentLengthMessageBody(keepAlive, contentLength, context));
            }

            // If we got here, request contains no Content-Length or Transfer-Encoding header.
            // Reject with 411 Length Required.
            if (context.Method == HttpMethod.Post || context.Method == HttpMethod.Put)
            {
                var requestRejectionReason = httpVersion == HttpVersion.Http11 ? RequestRejectionReason.LengthRequired : RequestRejectionReason.LengthRequiredHttp10;
                KestrelBadHttpRequestException.Throw(requestRejectionReason, context.Method);
            }

            context.OnTrailersComplete(); // No trailers for these.
            return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose);
        }
コード例 #19
0
    public override async ValueTask <ReadResult> ReadAsyncInternal(CancellationToken cancellationToken = default)
    {
        VerifyIsNotReading();

        if (_readCompleted)
        {
            _isReading = true;
            return(new ReadResult(_readResult.Buffer, Interlocked.Exchange(ref _userCanceled, 0) == 1, isCompleted: true));
        }

        // The issue is that TryRead can get a canceled read result
        // which is unknown to StartTimingReadAsync.
        if (_context.RequestTimedOut)
        {
            KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTimeout);
        }

        await TryStartAsync();

        // The while(true) loop is required because the Http1 connection calls CancelPendingRead to unblock
        // the call to StartTimingReadAsync to check if the request timed out.
        // However, if the user called CancelPendingRead, we want that to return a canceled ReadResult
        // We internally track an int for that.
        while (true)
        {
            try
            {
                var readAwaitable = _context.Input.ReadAsync(cancellationToken);

                _isReading  = true;
                _readResult = await StartTimingReadAsync(readAwaitable, cancellationToken);
            }
            catch (ConnectionAbortedException ex)
            {
                _isReading = false;
                throw new TaskCanceledException("The request was aborted", ex);
            }

            void ResetReadingState()
            {
                // Reset the timing read here for the next call to read.
                StopTimingRead(0);

                if (!_cannotResetInputPipe)
                {
                    _isReading = false;
                    _context.Input.AdvanceTo(_readResult.Buffer.Start);
                }
            }

            if (_context.RequestTimedOut)
            {
                ResetReadingState();
                KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTimeout);
            }

            if (_readResult.IsCompleted)
            {
                ResetReadingState();
                ThrowUnexpectedEndOfRequestContent();
            }

            // Ignore the canceled readResult if it wasn't canceled by the user.
            // Normally we do not return a canceled ReadResult unless CancelPendingRead was called on the request body PipeReader itself,
            // but if the last call to AdvanceTo examined data it did not consume, we cannot reset the state of the Input pipe.
            // https://github.com/dotnet/aspnetcore/issues/19476
            if (!_readResult.IsCanceled || Interlocked.Exchange(ref _userCanceled, 0) == 1 || _cannotResetInputPipe)
            {
                var returnedReadResultLength = CreateReadResultFromConnectionReadResult();

                // Don't count bytes belonging to the next request, since read rate timeouts are done on a per-request basis.
                StopTimingRead(returnedReadResultLength);

                if (_readResult.IsCompleted)
                {
                    TryStop();
                }

                break;
            }

            ResetReadingState();
        }

        return(_readResult);
    }
コード例 #20
0
        public override async ValueTask <ReadResult> ReadAsyncInternal(CancellationToken cancellationToken = default)
        {
            if (_isReading)
            {
                throw new InvalidOperationException("Reading is already in progress.");
            }

            if (_readCompleted)
            {
                _isReading = true;
                return(new ReadResult(_readResult.Buffer, Interlocked.Exchange(ref _userCanceled, 0) == 1, _readResult.IsCompleted));
            }

            TryStart();

            // The while(true) loop is required because the Http1 connection calls CancelPendingRead to unblock
            // the call to StartTimingReadAsync to check if the request timed out.
            // However, if the user called CancelPendingRead, we want that to return a canceled ReadResult
            // We internally track an int for that.
            while (true)
            {
                // The issue is that TryRead can get a canceled read result
                // which is unknown to StartTimingReadAsync.
                if (_context.RequestTimedOut)
                {
                    KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTimeout);
                }

                try
                {
                    var readAwaitable = _context.Input.ReadAsync(cancellationToken);

                    _isReading  = true;
                    _readResult = await StartTimingReadAsync(readAwaitable, cancellationToken);
                }
                catch (ConnectionAbortedException ex)
                {
                    _isReading = false;
                    throw new TaskCanceledException("The request was aborted", ex);
                }

                void ResetReadingState()
                {
                    _isReading = false;
                    // Reset the timing read here for the next call to read.
                    StopTimingRead(0);
                    _context.Input.AdvanceTo(_readResult.Buffer.Start);
                }

                if (_context.RequestTimedOut)
                {
                    ResetReadingState();
                    KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTimeout);
                }

                if (_readResult.IsCompleted)
                {
                    ResetReadingState();
                    ThrowUnexpectedEndOfRequestContent();
                }

                // Ignore the canceled readResult if it wasn't canceled by the user.
                if (!_readResult.IsCanceled || Interlocked.Exchange(ref _userCanceled, 0) == 1)
                {
                    var returnedReadResultLength = CreateReadResultFromConnectionReadResult();

                    // Don't count bytes belonging to the next request, since read rate timeouts are done on a per-request basis.
                    StopTimingRead(returnedReadResultLength);

                    if (_readResult.IsCompleted)
                    {
                        TryStop();
                    }

                    break;
                }

                ResetReadingState();
            }

            return(_readResult);
        }