Use one SequenceReader+Rewind rather than 2 (#8076)
This commit is contained in:
parent
3dc2be22c5
commit
f10680a37a
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue