diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs index bdbdc3c481..815ebc284b 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs @@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) { - OnAdvance(_readResult, consumed, examined); + TrackConsumedAndExaminedBytes(_readResult, consumed, examined); _requestBodyPipe.Reader.AdvanceTo(consumed, examined); } @@ -351,7 +351,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http consumed = reader.Position; examined = reader.Position; - AddAndCheckConsumedBytes(reader.Consumed); + AddAndCheckObservedBytes(reader.Consumed); _inputLength = chunkSize; _mode = Mode.Extension; return; @@ -370,7 +370,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { consumed = reader.Position; - AddAndCheckConsumedBytes(reader.Consumed); + AddAndCheckObservedBytes(reader.Consumed); _inputLength = chunkSize; _mode = chunkSize > 0 ? Mode.Data : Mode.Trailer; return; @@ -398,7 +398,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http // End marker not found yet consumed = buffer.End; examined = buffer.End; - AddAndCheckConsumedBytes(buffer.Length); + AddAndCheckObservedBytes(buffer.Length); return; }; @@ -410,7 +410,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { consumed = extensionCursor; examined = buffer.End; - AddAndCheckConsumedBytes(charsToByteCRExclusive); + AddAndCheckObservedBytes(charsToByteCRExclusive); return; } @@ -424,14 +424,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http consumed = suffixBuffer.End; examined = suffixBuffer.End; - AddAndCheckConsumedBytes(charsToByteCRExclusive + 2); + AddAndCheckObservedBytes(charsToByteCRExclusive + 2); } else { // Don't consume suffixSpan[1] in case it is also a \r. buffer = buffer.Slice(charsToByteCRExclusive + 1); consumed = extensionCursor; - AddAndCheckConsumedBytes(charsToByteCRExclusive + 1); + AddAndCheckObservedBytes(charsToByteCRExclusive + 1); } } while (_mode == Mode.Extension); } @@ -445,7 +445,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http Copy(buffer.Slice(0, actual), writableBuffer); _inputLength -= actual; - AddAndCheckConsumedBytes(actual); + AddAndCheckObservedBytes(actual); if (_inputLength == 0) { @@ -472,7 +472,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http if (suffixSpan[0] == '\r' && suffixSpan[1] == '\n') { consumed = suffixBuffer.End; - AddAndCheckConsumedBytes(2); + AddAndCheckObservedBytes(2); _mode = Mode.Prefix; } else @@ -500,7 +500,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http if (trailerSpan[0] == '\r' && trailerSpan[1] == '\n') { consumed = trailerBuffer.End; - AddAndCheckConsumedBytes(2); + AddAndCheckObservedBytes(2); _mode = Mode.Complete; // No trailers _context.OnTrailersComplete(); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/Http1ContentLengthMessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http/Http1ContentLengthMessageBody.cs index b0d1bf7846..728ce3337c 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/Http1ContentLengthMessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/Http1ContentLengthMessageBody.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { private ReadResult _readResult; private readonly long _contentLength; - private long _inputLength; + private long _unexaminedInputLength; private bool _readCompleted; private bool _isReading; private int _userCanceled; @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { RequestKeepAlive = keepAlive; _contentLength = contentLength; - _inputLength = _contentLength; + _unexaminedInputLength = _contentLength; } public override ValueTask ReadAsync(CancellationToken cancellationToken = default) @@ -181,7 +181,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private long CreateReadResultFromConnectionReadResult() { var initialLength = _readResult.Buffer.Length; - var maxLength = _inputLength + _examinedUnconsumedBytes; + var maxLength = _unexaminedInputLength + _examinedUnconsumedBytes; if (initialLength < maxLength) { @@ -226,7 +226,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return; } - _inputLength -= OnAdvance(_readResult, consumed, examined); + _unexaminedInputLength -= TrackConsumedAndExaminedBytes(_readResult, consumed, examined); _context.Input.AdvanceTo(consumed, examined); } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/MessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http/MessageBody.cs index 4f55f3de74..08f13a5959 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/MessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/MessageBody.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private readonly HttpProtocol _context; private bool _send100Continue = true; - private long _consumedBytes; + private long _observedBytes; private bool _stopped; protected bool _timingEnabled; @@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http public virtual void Reset() { _send100Continue = true; - _consumedBytes = 0; + _observedBytes = 0; _stopped = false; _timingEnabled = false; _backpressure = false; @@ -160,15 +160,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { } - protected virtual void OnDataRead(long bytesRead) + protected void AddAndCheckObservedBytes(long observedBytes) { - } + _observedBytes += observedBytes; - protected void AddAndCheckConsumedBytes(long consumedBytes) - { - _consumedBytes += consumedBytes; - - if (_consumedBytes > _context.MaxRequestBodySize) + if (_observedBytes > _context.MaxRequestBodySize) { KestrelBadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge); } @@ -209,7 +205,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http } } - protected long OnAdvance(ReadResult readResult, SequencePosition consumed, SequencePosition examined) + protected long TrackConsumedAndExaminedBytes(ReadResult readResult, SequencePosition consumed, SequencePosition examined) { // This code path is fairly hard to understand so let's break it down with an example // ReadAsync returns a ReadResult of length 50. @@ -252,18 +248,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http totalLength = readResult.Buffer.Length; } - var newlyExamined = examinedLength - _examinedUnconsumedBytes; - - if (newlyExamined > 0) - { - OnDataRead(newlyExamined); - _examinedUnconsumedBytes += newlyExamined; - } - - _examinedUnconsumedBytes -= consumedLength; + var newlyExaminedBytes = examinedLength - _examinedUnconsumedBytes; + _examinedUnconsumedBytes += newlyExaminedBytes - consumedLength; _alreadyTimedBytes = totalLength - consumedLength; - return newlyExamined; + return newlyExaminedBytes; } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs index 3d1dead1b9..53d88f4ff9 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs @@ -39,13 +39,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } } - protected override void OnDataRead(long bytesRead) - { - // The HTTP/2 flow control window cannot be larger than 2^31-1 which limits bytesRead. - _context.OnDataRead((int)bytesRead); - AddAndCheckConsumedBytes(bytesRead); - } - public override void Reset() { base.Reset(); @@ -59,8 +52,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) { - OnAdvance(_readResult, consumed, examined); + var newlyExaminedBytes = TrackConsumedAndExaminedBytes(_readResult, consumed, examined); + + // Ensure we consume data from the RequestBodyPipe before sending WINDOW_UPDATES to the client. _context.RequestBodyPipe.Reader.AdvanceTo(consumed, examined); + + // The HTTP/2 flow control window cannot be larger than 2^31-1 which limits bytesRead. + _context.OnDataRead((int)newlyExaminedBytes); + AddAndCheckObservedBytes(newlyExaminedBytes); } public override bool TryRead(out ReadResult readResult) diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs index 1b22820737..b72a01a5e6 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs @@ -446,7 +446,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 var flushTask = RequestBodyPipe.Writer.FlushAsync(); // It shouldn't be possible for the RequestBodyPipe to fill up an return an incomplete task if // _inputFlowControl.Advance() didn't throw. - Debug.Assert(flushTask.IsCompleted); + Debug.Assert(flushTask.IsCompletedSuccessfully); } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3MessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3MessageBody.cs index 77efd60e7b..96c6bc8aee 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3MessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3MessageBody.cs @@ -33,11 +33,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3 { } - protected override void OnDataRead(long bytesRead) - { - AddAndCheckConsumedBytes(bytesRead); - } - public static MessageBody For(Http3Stream context) { return new Http3MessageBody(context); @@ -50,8 +45,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3 public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) { - OnAdvance(_readResult, consumed, examined); + var newlyExaminedBytes = TrackConsumedAndExaminedBytes(_readResult, consumed, examined); + _context.RequestBodyPipe.Reader.AdvanceTo(consumed, examined); + + AddAndCheckObservedBytes(newlyExaminedBytes); } public override bool TryRead(out ReadResult readResult)