Use one SequenceReader+Rewind rather than 2 (#8076)

This commit is contained in:
Ben Adams 2019-06-07 21:15:27 +01:00 committed by Justin Kotalik
parent 3dc2be22c5
commit f10680a37a
8 changed files with 210 additions and 186 deletions

View File

@ -228,9 +228,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
public HttpParser() { } public HttpParser() { }
public HttpParser(bool showErrorDetails) { } public HttpParser(bool showErrorDetails) { }
bool Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.IHttpParser<TRequestHandler>.ParseHeaders(TRequestHandler handler, in System.Buffers.ReadOnlySequence<byte> buffer, out System.SequencePosition consumed, out System.SequencePosition examined, out int consumedBytes) { throw null; }
bool Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.IHttpParser<TRequestHandler>.ParseRequestLine(TRequestHandler handler, in System.Buffers.ReadOnlySequence<byte> buffer, out System.SequencePosition consumed, out System.SequencePosition examined) { throw null; } bool Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.IHttpParser<TRequestHandler>.ParseRequestLine(TRequestHandler handler, in System.Buffers.ReadOnlySequence<byte> buffer, out System.SequencePosition consumed, out System.SequencePosition examined) { throw null; }
public bool ParseHeaders(TRequestHandler handler, in System.Buffers.ReadOnlySequence<byte> buffer, out System.SequencePosition consumed, out System.SequencePosition examined, out int consumedBytes) { throw null; } public bool ParseHeaders(TRequestHandler handler, ref System.Buffers.SequenceReader<byte> reader) { throw null; }
public bool ParseRequestLine(TRequestHandler handler, in System.Buffers.ReadOnlySequence<byte> buffer, out System.SequencePosition consumed, out System.SequencePosition examined) { throw null; } public bool ParseRequestLine(TRequestHandler handler, in System.Buffers.ReadOnlySequence<byte> buffer, out System.SequencePosition consumed, out System.SequencePosition examined) { throw null; }
} }
public enum HttpScheme public enum HttpScheme
@ -253,7 +252,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} }
public partial interface IHttpParser<TRequestHandler> where TRequestHandler : Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.IHttpHeadersHandler, Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.IHttpRequestLineHandler public partial interface IHttpParser<TRequestHandler> where TRequestHandler : Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.IHttpHeadersHandler, Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.IHttpRequestLineHandler
{ {
bool ParseHeaders(TRequestHandler handler, in System.Buffers.ReadOnlySequence<byte> buffer, out System.SequencePosition consumed, out System.SequencePosition examined, out int consumedBytes); bool ParseHeaders(TRequestHandler handler, ref System.Buffers.SequenceReader<byte> reader);
bool ParseRequestLine(TRequestHandler handler, in System.Buffers.ReadOnlySequence<byte> buffer, out System.SequencePosition consumed, out System.SequencePosition examined); bool ParseRequestLine(TRequestHandler handler, in System.Buffers.ReadOnlySequence<byte> buffer, out System.SequencePosition consumed, out System.SequencePosition examined);
} }
public partial interface IHttpRequestLineHandler public partial interface IHttpRequestLineHandler

View File

@ -189,32 +189,77 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return result; return result;
} }
public bool TakeMessageHeaders(ReadOnlySequence<byte> buffer, bool trailers, out SequencePosition consumed, out SequencePosition examined) public bool TakeMessageHeaders(in ReadOnlySequence<byte> buffer, bool trailers, out SequencePosition consumed, out SequencePosition examined)
{ {
// Make sure the buffer is limited // Make sure the buffer is limited
bool overLength = false; if (buffer.Length > _remainingRequestHeadersBytesAllowed)
if (buffer.Length >= _remainingRequestHeadersBytesAllowed)
{ {
buffer = buffer.Slice(buffer.Start, _remainingRequestHeadersBytesAllowed); return TrimAndTakeMessageHeaders(buffer, trailers, out consumed, out examined);
// If we sliced it means the current buffer bigger than what we're
// allowed to look at
overLength = true;
} }
var result = _parser.ParseHeaders(new Http1ParsingHandler(this, trailers), buffer, out consumed, out examined, out var consumedBytes); var reader = new SequenceReader<byte>(buffer);
_remainingRequestHeadersBytesAllowed -= consumedBytes; var result = false;
try
{
result = _parser.ParseHeaders(new Http1ParsingHandler(this, trailers), ref reader);
if (!result && overLength) if (result)
{ {
BadHttpRequestException.Throw(RequestRejectionReason.HeadersExceedMaxTotalSize); TimeoutControl.CancelTimeout();
}
return result;
} }
if (result) finally
{ {
TimeoutControl.CancelTimeout(); consumed = reader.Position;
_remainingRequestHeadersBytesAllowed -= (int)reader.Consumed;
if (result)
{
examined = consumed;
}
else
{
examined = buffer.End;
}
} }
return result; bool TrimAndTakeMessageHeaders(in ReadOnlySequence<byte> buffer, bool trailers, out SequencePosition consumed, out SequencePosition examined)
{
var trimmedBuffer = buffer.Slice(buffer.Start, _remainingRequestHeadersBytesAllowed);
var reader = new SequenceReader<byte>(trimmedBuffer);
var result = false;
try
{
result = _parser.ParseHeaders(new Http1ParsingHandler(this, trailers), ref reader);
if (!result)
{
// We read the maximum allowed but didn't complete the headers.
BadHttpRequestException.Throw(RequestRejectionReason.HeadersExceedMaxTotalSize);
}
TimeoutControl.CancelTimeout();
return result;
}
finally
{
consumed = reader.Position;
_remainingRequestHeadersBytesAllowed -= (int)reader.Consumed;
if (result)
{
examined = consumed;
}
else
{
examined = trimmedBuffer.End;
}
}
}
} }
public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded) public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)

View File

@ -5,6 +5,7 @@ using System;
using System.Buffers; using System.Buffers;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
@ -174,149 +175,128 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
handler.OnStartLine(method, httpVersion, targetBuffer, pathBuffer, query, customMethod, pathEncoded); handler.OnStartLine(method, httpVersion, targetBuffer, pathBuffer, query, customMethod, pathEncoded);
} }
public unsafe bool ParseHeaders(TRequestHandler handler, in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined, out int consumedBytes) public unsafe bool ParseHeaders(TRequestHandler handler, ref SequenceReader<byte> reader)
{ {
consumed = buffer.Start; while (!reader.End)
examined = buffer.End;
consumedBytes = 0;
var bufferEnd = buffer.End;
var reader = new SequenceReader<byte>(buffer);
var start = default(SequenceReader<byte>);
var done = false;
try
{ {
while (!reader.End) var span = reader.UnreadSpan;
while (span.Length > 0)
{ {
var span = reader.CurrentSpan; var ch1 = (byte)0;
var remaining = span.Length - reader.CurrentSpanIndex; var ch2 = (byte)0;
var readAhead = 0;
fixed (byte* pBuffer = span) // Fast path, we're still looking at the same span
if (span.Length >= 2)
{ {
while (remaining > 0) 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)
{ {
var index = reader.CurrentSpanIndex; // If we got 2 bytes from the span directly so skip ahead 2 so that
byte ch1; // the reader's state matches what we expect
byte ch2; if (readAhead == 0)
var readAhead = false;
var readSecond = true;
// Fast path, we're still looking at the same span
if (remaining >= 2)
{ {
ch1 = pBuffer[index]; reader.Advance(2);
ch2 = pBuffer[index + 1];
}
else
{
// Store the reader before we look ahead 2 bytes (probably straddling
// spans)
start = reader;
// Possibly split across spans
reader.TryRead(out ch1);
readSecond = reader.TryRead(out ch2);
readAhead = true;
} }
if (ch1 == ByteCR) // Double CRLF found, so end of headers.
handler.OnHeadersComplete();
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.
BadHttpRequestException.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.IndexOf(ByteLF) + 1;
if (length > 0)
{
// Potentially found the end, or an invalid header.
fixed (byte* pHeader = span)
{ {
// Check for final CRLF.
if (!readSecond)
{
// Reset the reader so we don't consume anything
reader = start;
return false;
}
else 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)
{
reader.Advance(2);
}
done = true;
handler.OnHeadersComplete();
return true;
}
// Headers don't end in CRLF line.
BadHttpRequestException.Throw(RequestRejectionReason.InvalidRequestHeadersNoCRLF);
}
// We moved the reader so look ahead 2 bytes so reset both the reader
// and the index
if (readAhead)
{
reader = start;
index = reader.CurrentSpanIndex;
}
var endIndex = new Span<byte>(pBuffer + index, remaining).IndexOf(ByteLF);
var length = 0;
if (endIndex != -1)
{
length = endIndex + 1;
var pHeader = pBuffer + index;
TakeSingleHeader(pHeader, length, handler); TakeSingleHeader(pHeader, length, handler);
} }
else // Read the header sucessfully, skip the reader forward past the header line.
{
var current = reader.Position;
var currentSlice = buffer.Slice(current, bufferEnd);
var lineEndPosition = currentSlice.PositionOf(ByteLF);
// Split buffers
if (lineEndPosition == null)
{
// Not there
return false;
}
var lineEnd = lineEndPosition.Value;
// Make sure LF is included in lineEnd
lineEnd = buffer.GetPosition(1, lineEnd);
var headerSpan = buffer.Slice(current, lineEnd).ToSpan();
length = headerSpan.Length;
fixed (byte* pHeader = headerSpan)
{
TakeSingleHeader(pHeader, length, handler);
}
// We're going to the next span after this since we know we crossed spans here
// so mark the remaining as equal to the headerSpan so that we end up at 0
// on the next iteration
remaining = length;
}
// Skip the reader forward past the header line
reader.Advance(length); reader.Advance(length);
remaining -= length; span = span.Slice(length);
} }
} }
}
return false; // End not found in current span
if (length <= 0)
{
// We moved the reader to look ahead 2 bytes so rewind the reader
if (readAhead > 0)
{
reader.Rewind(readAhead);
}
length = ParseMultiSpanHeader(handler, ref reader);
if (length < 0)
{
// Not there
return false;
}
reader.Advance(length);
// As we crossed spans set the current span to default
// so we move to the next span on the next iteration
span = default;
}
}
} }
finally
return false;
}
private unsafe int ParseMultiSpanHeader(TRequestHandler handler, ref SequenceReader<byte> reader)
{
var buffer = reader.Sequence;
var currentSlice = buffer.Slice(reader.Position, reader.Remaining);
var lineEndPosition = currentSlice.PositionOf(ByteLF);
// Split buffers
if (lineEndPosition == null)
{ {
consumed = reader.Position; // Not there
consumedBytes = (int)reader.Consumed; return -1;
if (done)
{
examined = consumed;
}
} }
var lineEnd = lineEndPosition.Value;
// Make sure LF is included in lineEnd
lineEnd = buffer.GetPosition(1, lineEnd);
var headerSpan = buffer.Slice(reader.Position, lineEnd).ToSpan();
var length = headerSpan.Length;
fixed (byte* pHeader = headerSpan)
{
TakeSingleHeader(pHeader, length, handler);
}
return length;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -339,7 +319,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
if (index == length || sawWhitespace) if (index == length || sawWhitespace)
{ {
RejectRequestHeader(headerLine, length); // Set to -1 to indicate invalid.
index = -1;
} }
return index; return index;
@ -352,17 +333,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var valueEnd = length - 3; var valueEnd = length - 3;
var nameEnd = FindEndOfName(headerLine, length); var nameEnd = FindEndOfName(headerLine, length);
// Header name is empty // Header name is empty, invalid, or doesn't end in CRLF
if (nameEnd == 0) if (nameEnd <= 0 || headerLine[valueEnd + 2] != ByteLF || headerLine[valueEnd + 1] != ByteCR)
{
RejectRequestHeader(headerLine, length);
}
if (headerLine[valueEnd + 2] != ByteLF)
{
RejectRequestHeader(headerLine, length);
}
if (headerLine[valueEnd + 1] != ByteCR)
{ {
RejectRequestHeader(headerLine, length); RejectRequestHeader(headerLine, length);
} }

View File

@ -10,6 +10,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
bool ParseRequestLine(TRequestHandler handler, in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined); bool ParseRequestLine(TRequestHandler handler, in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined);
bool ParseHeaders(TRequestHandler handler, in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined, out int consumedBytes); bool ParseHeaders(TRequestHandler handler, ref SequenceReader<byte> reader);
} }
} }

View File

@ -182,7 +182,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes(rawHeaders)); var buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes(rawHeaders));
var requestHandler = new RequestHandler(); var requestHandler = new RequestHandler();
Assert.False(parser.ParseHeaders(requestHandler, buffer, out var consumed, out var examined, out var consumedBytes)); var reader = new SequenceReader<byte>(buffer);
Assert.False(parser.ParseHeaders(requestHandler, ref reader));
} }
[Theory] [Theory]
@ -207,11 +208,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes(rawHeaders)); var buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes(rawHeaders));
var requestHandler = new RequestHandler(); var requestHandler = new RequestHandler();
parser.ParseHeaders(requestHandler, buffer, out var consumed, out var examined, out var consumedBytes); var reader = new SequenceReader<byte>(buffer);
Assert.Equal(buffer.Length, buffer.Slice(consumed).Length); Assert.False(parser.ParseHeaders(requestHandler, ref reader));
Assert.True(buffer.Slice(examined).IsEmpty);
Assert.Equal(0, consumedBytes); Assert.Equal(buffer.Length, buffer.Slice(reader.Consumed).Length);
Assert.Equal(0, reader.Consumed);
} }
[Fact] [Fact]
@ -297,18 +299,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
const string headerLine = "Header: value\r\n\r"; const string headerLine = "Header: value\r\n\r";
var buffer1 = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes(headerLine)); var buffer1 = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes(headerLine));
var requestHandler = new RequestHandler(); var requestHandler = new RequestHandler();
Assert.False(parser.ParseHeaders(requestHandler, buffer1, out var consumed, out var examined, out var consumedBytes)); var reader1 = new SequenceReader<byte>(buffer1);
Assert.False(parser.ParseHeaders(requestHandler, ref reader1));
Assert.Equal(buffer1.GetPosition(headerLine.Length - 1), consumed); Assert.Equal(buffer1.GetPosition(headerLine.Length - 1), reader1.Position);
Assert.Equal(buffer1.End, examined); Assert.Equal(headerLine.Length - 1, reader1.Consumed);
Assert.Equal(headerLine.Length - 1, consumedBytes);
var buffer2 = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes("\r\n")); var buffer2 = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes("\r\n"));
Assert.True(parser.ParseHeaders(requestHandler, buffer2, out consumed, out examined, out consumedBytes)); var reader2 = new SequenceReader<byte>(buffer2);
Assert.True(parser.ParseHeaders(requestHandler, ref reader2));
Assert.True(buffer2.Slice(consumed).IsEmpty); Assert.True(buffer2.Slice(reader2.Position).IsEmpty);
Assert.True(buffer2.Slice(examined).IsEmpty); Assert.Equal(2, reader2.Consumed);
Assert.Equal(2, consumedBytes);
} }
[Theory] [Theory]
@ -325,7 +327,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var requestHandler = new RequestHandler(); var requestHandler = new RequestHandler();
var exception = Assert.Throws<BadHttpRequestException>(() => var exception = Assert.Throws<BadHttpRequestException>(() =>
parser.ParseHeaders(requestHandler, buffer, out var consumed, out var examined, out var consumedBytes)); {
var reader = new SequenceReader<byte>(buffer);
parser.ParseHeaders(requestHandler, ref reader);
});
Assert.Equal(expectedExceptionMessage, exception.Message); Assert.Equal(expectedExceptionMessage, exception.Message);
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode); Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
@ -364,7 +369,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes("Header: value\n\r\n")); buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes("Header: value\n\r\n"));
exception = Assert.Throws<BadHttpRequestException>(() => exception = Assert.Throws<BadHttpRequestException>(() =>
parser.ParseHeaders(requestHandler, buffer, out var consumed, out var examined, out var consumedBytes)); {
var reader = new SequenceReader<byte>(buffer);
parser.ParseHeaders(requestHandler, ref reader);
});
Assert.Equal(CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(string.Empty), exception.Message); Assert.Equal(CoreStrings.FormatBadRequest_InvalidRequestHeader_Detail(string.Empty), exception.Message);
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode); Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
@ -393,7 +401,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var buffer = BytePerSegmentTestSequenceFactory.Instance.CreateWithContent("Host:\r\nConnection: keep-alive\r\n\r\n"); var buffer = BytePerSegmentTestSequenceFactory.Instance.CreateWithContent("Host:\r\nConnection: keep-alive\r\n\r\n");
var requestHandler = new RequestHandler(); var requestHandler = new RequestHandler();
var result = parser.ParseHeaders(requestHandler, buffer, out var consumed, out var examined, out _); var reader = new SequenceReader<byte>(buffer);
var result = parser.ParseHeaders(requestHandler, ref reader);
Assert.True(result); Assert.True(result);
} }
@ -405,7 +414,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var buffer = BytePerSegmentTestSequenceFactory.Instance.CreateWithContent("A:B\r\nB: C\r\n\r\n"); var buffer = BytePerSegmentTestSequenceFactory.Instance.CreateWithContent("A:B\r\nB: C\r\n\r\n");
var requestHandler = new RequestHandler(); var requestHandler = new RequestHandler();
var result = parser.ParseHeaders(requestHandler, buffer, out var consumed, out var examined, out _); var reader = new SequenceReader<byte>(buffer);
var result = parser.ParseHeaders(requestHandler, ref reader);
Assert.True(result); Assert.True(result);
} }
@ -419,14 +429,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes($"{headerName}:{rawHeaderValue}\r\n")); var buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes($"{headerName}:{rawHeaderValue}\r\n"));
var requestHandler = new RequestHandler(); var requestHandler = new RequestHandler();
parser.ParseHeaders(requestHandler, buffer, out var consumed, out var examined, out var consumedBytes); var reader = new SequenceReader<byte>(buffer);
Assert.False(parser.ParseHeaders(requestHandler, ref reader));
var pairs = requestHandler.Headers.ToArray(); var pairs = requestHandler.Headers.ToArray();
Assert.Single(pairs); Assert.Single(pairs);
Assert.Equal(headerName, pairs[0].Key); Assert.Equal(headerName, pairs[0].Key);
Assert.Equal(expectedHeaderValue, pairs[0].Value); Assert.Equal(expectedHeaderValue, pairs[0].Value);
Assert.True(buffer.Slice(consumed).IsEmpty); Assert.True(buffer.Slice(reader.Position).IsEmpty);
Assert.True(buffer.Slice(examined).IsEmpty);
} }
private void VerifyRawHeaders(string rawHeaders, IEnumerable<string> expectedHeaderNames, IEnumerable<string> expectedHeaderValues) private void VerifyRawHeaders(string rawHeaders, IEnumerable<string> expectedHeaderNames, IEnumerable<string> expectedHeaderValues)
@ -437,15 +447,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes(rawHeaders)); var buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes(rawHeaders));
var requestHandler = new RequestHandler(); var requestHandler = new RequestHandler();
parser.ParseHeaders(requestHandler, buffer, out var consumed, out var examined, out var consumedBytes); var reader = new SequenceReader<byte>(buffer);
Assert.True(parser.ParseHeaders(requestHandler,ref reader));
var parsedHeaders = requestHandler.Headers.ToArray(); var parsedHeaders = requestHandler.Headers.ToArray();
Assert.Equal(expectedHeaderNames.Count(), parsedHeaders.Length); Assert.Equal(expectedHeaderNames.Count(), parsedHeaders.Length);
Assert.Equal(expectedHeaderNames, parsedHeaders.Select(t => t.Key)); Assert.Equal(expectedHeaderNames, parsedHeaders.Select(t => t.Key));
Assert.Equal(expectedHeaderValues, parsedHeaders.Select(t => t.Value)); Assert.Equal(expectedHeaderValues, parsedHeaders.Select(t => t.Value));
Assert.True(buffer.Slice(consumed).IsEmpty); Assert.True(buffer.Slice(reader.Position).IsEmpty);
Assert.True(buffer.Slice(examined).IsEmpty);
} }
private IHttpParser<RequestHandler> CreateParser(IKestrelTrace log) => new HttpParser<RequestHandler>(log.IsEnabled(LogLevel.Information)); private IHttpParser<RequestHandler> CreateParser(IKestrelTrace log) => new HttpParser<RequestHandler>(log.IsEnabled(LogLevel.Information));

View File

@ -83,8 +83,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
} }
_buffer = _buffer.Slice(consumed, _buffer.End); _buffer = _buffer.Slice(consumed, _buffer.End);
var reader = new SequenceReader<byte>(_buffer);
if (!_parser.ParseHeaders(new Adapter(this), _buffer, out consumed, out examined, out var consumedBytes)) if (!_parser.ParseHeaders(new Adapter(this), ref reader))
{ {
ErrorUtilities.ThrowInvalidRequestHeaders(); ErrorUtilities.ThrowInvalidRequestHeaders();
} }

View File

@ -58,7 +58,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
_buffer = _buffer.Slice(consumed, _buffer.End); _buffer = _buffer.Slice(consumed, _buffer.End);
if (!_parser.ParseHeaders(new Adapter(this), _buffer, out consumed, out examined, out var consumedBytes)) var reader = new SequenceReader<byte>(_buffer);
if (!_parser.ParseHeaders(new Adapter(this), ref reader))
{ {
ErrorUtilities.ThrowInvalidRequestHeaders(); ErrorUtilities.ThrowInvalidRequestHeaders();
} }

View File

@ -21,17 +21,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
public static readonly NullParser<Http1ParsingHandler> Instance = new NullParser<Http1ParsingHandler>(); public static readonly NullParser<Http1ParsingHandler> Instance = new NullParser<Http1ParsingHandler>();
public bool ParseHeaders(TRequestHandler handler, in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined, out int consumedBytes) public bool ParseHeaders(TRequestHandler handler, ref SequenceReader<byte> reader)
{ {
handler.OnHeader(new Span<byte>(_hostHeaderName), new Span<byte>(_hostHeaderValue)); handler.OnHeader(new Span<byte>(_hostHeaderName), new Span<byte>(_hostHeaderValue));
handler.OnHeader(new Span<byte>(_acceptHeaderName), new Span<byte>(_acceptHeaderValue)); handler.OnHeader(new Span<byte>(_acceptHeaderName), new Span<byte>(_acceptHeaderValue));
handler.OnHeader(new Span<byte>(_connectionHeaderName), new Span<byte>(_connectionHeaderValue)); handler.OnHeader(new Span<byte>(_connectionHeaderName), new Span<byte>(_connectionHeaderValue));
handler.OnHeadersComplete(); handler.OnHeadersComplete();
consumedBytes = 0;
consumed = buffer.Start;
examined = buffer.End;
return true; return true;
} }