Speed up ParseRequestLine (#1463)
This commit is contained in:
parent
49d058a997
commit
941d396942
|
|
@ -1216,15 +1216,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
Log.ApplicationError(ConnectionId, ex);
|
||||
}
|
||||
|
||||
public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod)
|
||||
public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
|
||||
{
|
||||
// URIs are always encoded/escaped to ASCII https://tools.ietf.org/html/rfc3986#page-11
|
||||
// Multibyte Internationalized Resource Identifiers (IRIs) are first converted to utf8;
|
||||
// then encoded/escaped to ASCII https://www.ietf.org/rfc/rfc3987.txt "Mapping of IRIs to URIs"
|
||||
string requestUrlPath;
|
||||
string rawTarget;
|
||||
var needDecode = path.IndexOf(BytePercentage) >= 0;
|
||||
if (needDecode)
|
||||
if (pathEncoded)
|
||||
{
|
||||
// Read raw target before mutating memory.
|
||||
rawTarget = target.GetAsciiStringNonNullCharacters();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
public interface IHttpRequestLineHandler
|
||||
{
|
||||
void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod);
|
||||
void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded);
|
||||
}
|
||||
}
|
||||
|
|
@ -34,219 +34,130 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
consumed = buffer.Start;
|
||||
examined = buffer.End;
|
||||
|
||||
ReadCursor end;
|
||||
Span<byte> span;
|
||||
|
||||
// If the buffer is a single span then use it to find the LF
|
||||
if (buffer.IsSingleSpan)
|
||||
// Prepare the first span
|
||||
var span = buffer.First.Span;
|
||||
var lineIndex = span.IndexOfVectorized(ByteLF);
|
||||
if (lineIndex >= 0)
|
||||
{
|
||||
var startLineSpan = buffer.First.Span;
|
||||
var lineIndex = startLineSpan.IndexOfVectorized(ByteLF);
|
||||
|
||||
if (lineIndex == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
end = buffer.Move(consumed, lineIndex + 1);
|
||||
span = startLineSpan.Slice(0, lineIndex + 1);
|
||||
consumed = buffer.Move(consumed, lineIndex + 1);
|
||||
span = span.Slice(0, lineIndex + 1);
|
||||
}
|
||||
else
|
||||
else if (buffer.IsSingleSpan || !TryGetNewLineSpan(ref buffer, ref span, out consumed))
|
||||
{
|
||||
var start = buffer.Start;
|
||||
if (ReadCursorOperations.Seek(start, buffer.End, out end, ByteLF) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move 1 byte past the \n
|
||||
end = buffer.Move(end, 1);
|
||||
var startLineBuffer = buffer.Slice(start, end);
|
||||
|
||||
span = startLineBuffer.ToSpan();
|
||||
// No request line end
|
||||
return false;
|
||||
}
|
||||
|
||||
var pathStart = -1;
|
||||
var queryStart = -1;
|
||||
var queryEnd = -1;
|
||||
var pathEnd = -1;
|
||||
var versionStart = -1;
|
||||
|
||||
var httpVersion = HttpVersion.Unknown;
|
||||
HttpMethod method;
|
||||
Span<byte> customMethod;
|
||||
var i = 0;
|
||||
var length = span.Length;
|
||||
var done = false;
|
||||
|
||||
// Fix and parse the span
|
||||
fixed (byte* data = &span.DangerousGetPinnableReference())
|
||||
{
|
||||
switch (StartLineState.KnownMethod)
|
||||
ParseRequestLine(handler, data, span.Length);
|
||||
}
|
||||
|
||||
examined = consumed;
|
||||
return true;
|
||||
}
|
||||
|
||||
private unsafe void ParseRequestLine<T>(T handler, byte* data, int length) where T : IHttpRequestLineHandler
|
||||
{
|
||||
int offset;
|
||||
Span<byte> customMethod;
|
||||
// Get Method and set the offset
|
||||
var method = HttpUtilities.GetKnownMethod(data, length, out offset);
|
||||
if (method == HttpMethod.Custom)
|
||||
{
|
||||
customMethod = GetUnknownMethod(data, length, out offset);
|
||||
}
|
||||
|
||||
// Skip space
|
||||
offset++;
|
||||
|
||||
byte ch = 0;
|
||||
// Target = Path and Query
|
||||
var pathEncoded = false;
|
||||
var pathStart = -1;
|
||||
for (; offset < length; offset++)
|
||||
{
|
||||
ch = data[offset];
|
||||
if (ch == ByteSpace)
|
||||
{
|
||||
case StartLineState.KnownMethod:
|
||||
if (span.GetKnownMethod(out method, out var methodLength))
|
||||
{
|
||||
// Update the index, current char, state and jump directly
|
||||
// to the next state
|
||||
i += methodLength + 1;
|
||||
if (pathStart == -1)
|
||||
{
|
||||
// Empty path is illegal
|
||||
RejectRequestLine(data, length);
|
||||
}
|
||||
|
||||
goto case StartLineState.Path;
|
||||
}
|
||||
goto case StartLineState.UnknownMethod;
|
||||
break;
|
||||
}
|
||||
else if (ch == ByteQuestionMark)
|
||||
{
|
||||
if (pathStart == -1)
|
||||
{
|
||||
// Empty path is illegal
|
||||
RejectRequestLine(data, length);
|
||||
}
|
||||
|
||||
case StartLineState.UnknownMethod:
|
||||
for (; i < length; i++)
|
||||
{
|
||||
var ch = data[i];
|
||||
break;
|
||||
}
|
||||
else if (ch == BytePercentage)
|
||||
{
|
||||
if (pathStart == -1)
|
||||
{
|
||||
// Path starting with % is illegal
|
||||
RejectRequestLine(data, length);
|
||||
}
|
||||
|
||||
if (ch == ByteSpace)
|
||||
{
|
||||
customMethod = span.Slice(0, i);
|
||||
|
||||
if (customMethod.Length == 0)
|
||||
{
|
||||
RejectRequestLine(span);
|
||||
}
|
||||
// Consume space
|
||||
i++;
|
||||
|
||||
goto case StartLineState.Path;
|
||||
}
|
||||
|
||||
if (!IsValidTokenChar((char)ch))
|
||||
{
|
||||
RejectRequestLine(span);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case StartLineState.Path:
|
||||
for (; i < length; i++)
|
||||
{
|
||||
var ch = data[i];
|
||||
if (ch == ByteSpace)
|
||||
{
|
||||
pathEnd = i;
|
||||
|
||||
if (pathStart == -1)
|
||||
{
|
||||
// Empty path is illegal
|
||||
RejectRequestLine(span);
|
||||
}
|
||||
|
||||
// No query string found
|
||||
queryStart = queryEnd = i;
|
||||
|
||||
// Consume space
|
||||
i++;
|
||||
|
||||
goto case StartLineState.KnownVersion;
|
||||
}
|
||||
else if (ch == ByteQuestionMark)
|
||||
{
|
||||
pathEnd = i;
|
||||
|
||||
if (pathStart == -1)
|
||||
{
|
||||
// Empty path is illegal
|
||||
RejectRequestLine(span);
|
||||
}
|
||||
|
||||
queryStart = i;
|
||||
goto case StartLineState.QueryString;
|
||||
}
|
||||
else if (ch == BytePercentage)
|
||||
{
|
||||
if (pathStart == -1)
|
||||
{
|
||||
RejectRequestLine(span);
|
||||
}
|
||||
}
|
||||
|
||||
if (pathStart == -1)
|
||||
{
|
||||
pathStart = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StartLineState.QueryString:
|
||||
for (; i < length; i++)
|
||||
{
|
||||
var ch = data[i];
|
||||
if (ch == ByteSpace)
|
||||
{
|
||||
queryEnd = i;
|
||||
|
||||
// Consume space
|
||||
i++;
|
||||
|
||||
goto case StartLineState.KnownVersion;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StartLineState.KnownVersion:
|
||||
// REVIEW: We don't *need* to slice here but it makes the API
|
||||
// nicer, slicing should be free :)
|
||||
if (span.Slice(i).GetKnownVersion(out httpVersion, out var versionLenght))
|
||||
{
|
||||
// Update the index, current char, state and jump directly
|
||||
// to the next state
|
||||
i += versionLenght + 1;
|
||||
goto case StartLineState.NewLine;
|
||||
}
|
||||
|
||||
versionStart = i;
|
||||
|
||||
goto case StartLineState.UnknownVersion;
|
||||
|
||||
case StartLineState.UnknownVersion:
|
||||
for (; i < length; i++)
|
||||
{
|
||||
var ch = data[i];
|
||||
if (ch == ByteCR)
|
||||
{
|
||||
var versionSpan = span.Slice(versionStart, i - versionStart);
|
||||
|
||||
if (versionSpan.Length == 0)
|
||||
{
|
||||
RejectRequestLine(span);
|
||||
}
|
||||
else
|
||||
{
|
||||
RejectRequest(RequestRejectionReason.UnrecognizedHTTPVersion,
|
||||
versionSpan.GetAsciiStringEscaped(32));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StartLineState.NewLine:
|
||||
if (data[i] != ByteLF)
|
||||
{
|
||||
RejectRequestLine(span);
|
||||
}
|
||||
i++;
|
||||
|
||||
goto case StartLineState.Complete;
|
||||
case StartLineState.Complete:
|
||||
done = true;
|
||||
break;
|
||||
pathEncoded = true;
|
||||
}
|
||||
else if (pathStart == -1)
|
||||
{
|
||||
pathStart = offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done)
|
||||
if (pathStart == -1)
|
||||
{
|
||||
RejectRequestLine(span);
|
||||
// End of path not found
|
||||
RejectRequestLine(data, length);
|
||||
}
|
||||
|
||||
var pathBuffer = span.Slice(pathStart, pathEnd - pathStart);
|
||||
var targetBuffer = span.Slice(pathStart, queryEnd - pathStart);
|
||||
var query = span.Slice(queryStart, queryEnd - queryStart);
|
||||
var pathBuffer = new Span<byte>(data + pathStart, offset - pathStart);
|
||||
|
||||
handler.OnStartLine(method, httpVersion, targetBuffer, pathBuffer, query, customMethod);
|
||||
var queryStart = offset;
|
||||
// Query string
|
||||
if (ch == ByteQuestionMark)
|
||||
{
|
||||
// We have a query string
|
||||
for (; offset < length; offset++)
|
||||
{
|
||||
ch = data[offset];
|
||||
if (ch == ByteSpace)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
consumed = end;
|
||||
examined = consumed;
|
||||
return true;
|
||||
var targetBuffer = new Span<byte>(data + pathStart, offset - pathStart);
|
||||
var query = new Span<byte>(data + queryStart, offset - queryStart);
|
||||
|
||||
// Consume space
|
||||
offset++;
|
||||
|
||||
// Version
|
||||
var httpVersion = HttpUtilities.GetKnownVersion(data + offset, length - offset);
|
||||
if (httpVersion == HttpVersion.Unknown)
|
||||
{
|
||||
RejectUnknownVersion(data, length, offset);
|
||||
}
|
||||
|
||||
// After version 8 bytes and cr 1 byte, expect lf
|
||||
if (data[offset + 8 + 1] != ByteLF)
|
||||
{
|
||||
RejectRequestLine(data, length);
|
||||
}
|
||||
|
||||
handler.OnStartLine(method, httpVersion, targetBuffer, pathBuffer, query, customMethod, pathEncoded);
|
||||
}
|
||||
|
||||
public unsafe bool ParseHeaders<T>(T handler, ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined, out int consumedBytes) where T : IHttpHeadersHandler
|
||||
|
|
@ -502,6 +413,48 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static bool TryGetNewLineSpan(ref ReadableBuffer buffer, ref Span<byte> span, out ReadCursor end)
|
||||
{
|
||||
var start = buffer.Start;
|
||||
if (ReadCursorOperations.Seek(start, buffer.End, out end, ByteLF) != -1)
|
||||
{
|
||||
// Move 1 byte past the \n
|
||||
end = buffer.Move(end, 1);
|
||||
span = buffer.Slice(start, end).ToSpan();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private unsafe Span<byte> GetUnknownMethod(byte* data, int length, out int methodLength)
|
||||
{
|
||||
methodLength = 0;
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var ch = data[i];
|
||||
|
||||
if (ch == ByteSpace)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
RejectRequestLine(data, length);
|
||||
}
|
||||
|
||||
methodLength = i;
|
||||
break;
|
||||
}
|
||||
else if (!IsValidTokenChar((char)ch))
|
||||
{
|
||||
RejectRequestLine(data, length);
|
||||
}
|
||||
}
|
||||
|
||||
return new Span<byte>(data, methodLength);
|
||||
}
|
||||
|
||||
private static bool IsValidTokenChar(char c)
|
||||
{
|
||||
// Determines if a character is valid as a 'token' as defined in the
|
||||
|
|
@ -532,9 +485,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
throw BadHttpRequestException.GetException(reason);
|
||||
}
|
||||
|
||||
public static void RejectRequest(RequestRejectionReason reason, string value)
|
||||
private unsafe void RejectUnknownVersion(byte* data, int length, int versionStart)
|
||||
{
|
||||
throw BadHttpRequestException.GetException(reason, value);
|
||||
throw GetRejectUnknownVersion(data, length, versionStart);
|
||||
}
|
||||
|
||||
private unsafe void RejectRequestLine(byte* data, int length)
|
||||
{
|
||||
throw GetRejectRequestLineException(new Span<byte>(data, length));
|
||||
}
|
||||
|
||||
private void RejectRequestLine(Span<byte> span)
|
||||
|
|
@ -549,6 +507,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
Log.IsEnabled(LogLevel.Information) ? span.GetAsciiStringEscaped(MaxRequestLineError) : string.Empty);
|
||||
}
|
||||
|
||||
private unsafe BadHttpRequestException GetRejectUnknownVersion(byte* data, int length, int versionStart)
|
||||
{
|
||||
var span = new Span<byte>(data, length);
|
||||
length -= versionStart;
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var ch = span[i + versionStart];
|
||||
if (ch == ByteCR)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return GetRejectRequestLineException(span);
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadHttpRequestException.GetException(RequestRejectionReason.UnrecognizedHTTPVersion,
|
||||
span.Slice(versionStart, i).GetAsciiStringEscaped(32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GetRejectRequestLineException(span);
|
||||
}
|
||||
|
||||
private unsafe void RejectRequestHeader(byte* headerLine, int length)
|
||||
{
|
||||
RejectRequestHeader(new Span<byte>(headerLine, length));
|
||||
|
|
@ -578,26 +560,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
// https://github.com/dotnet/coreclr/issues/7459#issuecomment-253965670
|
||||
return Vector.AsVectorByte(new Vector<uint>(vectorByte * 0x01010101u));
|
||||
}
|
||||
|
||||
private enum HeaderState
|
||||
{
|
||||
Name,
|
||||
Whitespace,
|
||||
ExpectValue,
|
||||
ExpectNewLine,
|
||||
Complete
|
||||
}
|
||||
|
||||
private enum StartLineState
|
||||
{
|
||||
KnownMethod,
|
||||
UnknownMethod,
|
||||
Path,
|
||||
QueryString,
|
||||
KnownVersion,
|
||||
UnknownVersion,
|
||||
NewLine,
|
||||
Complete
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,34 +147,46 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
|||
/// </remarks>
|
||||
/// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool GetKnownMethod(this Span<byte> span, out HttpMethod method, out int length)
|
||||
public static unsafe bool GetKnownMethod(this Span<byte> span, out HttpMethod method, out int length)
|
||||
{
|
||||
if (span.TryRead<uint>(out var possiblyGet))
|
||||
fixed (byte* data = &span.DangerousGetPinnableReference())
|
||||
{
|
||||
if (possiblyGet == _httpGetMethodInt)
|
||||
{
|
||||
length = 3;
|
||||
method = HttpMethod.Get;
|
||||
return true;
|
||||
}
|
||||
method = GetKnownMethod(data, span.Length, out length);
|
||||
return method != HttpMethod.Custom;
|
||||
}
|
||||
}
|
||||
|
||||
if (span.TryRead<ulong>(out var value))
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal unsafe static HttpMethod GetKnownMethod(byte* data, int length, out int methodLength)
|
||||
{
|
||||
methodLength = 0;
|
||||
if (length < sizeof(uint))
|
||||
{
|
||||
return HttpMethod.Custom;
|
||||
}
|
||||
else if (*(uint*)data == _httpGetMethodInt)
|
||||
{
|
||||
methodLength = 3;
|
||||
return HttpMethod.Get;
|
||||
}
|
||||
else if (length < sizeof(ulong))
|
||||
{
|
||||
return HttpMethod.Custom;
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = *(ulong*)data;
|
||||
foreach (var x in _knownMethods)
|
||||
{
|
||||
if ((value & x.Item1) == x.Item2)
|
||||
{
|
||||
method = x.Item3;
|
||||
length = x.Item4;
|
||||
return true;
|
||||
methodLength = x.Item4;
|
||||
return x.Item3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
method = HttpMethod.Custom;
|
||||
length = 0;
|
||||
return false;
|
||||
return HttpMethod.Custom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -189,36 +201,56 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
|||
/// </remarks>
|
||||
/// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool GetKnownVersion(this Span<byte> span, out HttpVersion knownVersion, out byte length)
|
||||
public static unsafe bool GetKnownVersion(this Span<byte> span, out HttpVersion knownVersion, out byte length)
|
||||
{
|
||||
if (span.TryRead<ulong>(out var version))
|
||||
fixed (byte* data = &span.DangerousGetPinnableReference())
|
||||
{
|
||||
if (version == _http11VersionLong)
|
||||
knownVersion = GetKnownVersion(data, span.Length);
|
||||
if (knownVersion != HttpVersion.Unknown)
|
||||
{
|
||||
length = sizeof(ulong);
|
||||
knownVersion = HttpVersion.Http11;
|
||||
}
|
||||
else if (version == _http10VersionLong)
|
||||
{
|
||||
length = sizeof(ulong);
|
||||
knownVersion = HttpVersion.Http10;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 0;
|
||||
knownVersion = HttpVersion.Unknown;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (span[sizeof(ulong)] == (byte)'\r')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
length = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks 9 bytes from <paramref name="location"/> correspond to a known HTTP version.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A "known HTTP version" Is is either HTTP/1.0 or HTTP/1.1.
|
||||
/// Since those fit in 8 bytes, they can be optimally looked up by reading those bytes as a long. Once
|
||||
/// in that format, it can be checked against the known versions.
|
||||
/// The Known versions will be checked with the required '\r'.
|
||||
/// To optimize performance the HTTP/1.1 will be checked first.
|
||||
/// </remarks>
|
||||
/// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal unsafe static HttpVersion GetKnownVersion(byte* location, int length)
|
||||
{
|
||||
HttpVersion knownVersion;
|
||||
var version = *(ulong*)location;
|
||||
if (length < sizeof(ulong) + 1 || location[sizeof(ulong)] != (byte)'\r')
|
||||
{
|
||||
knownVersion = HttpVersion.Unknown;
|
||||
}
|
||||
else if (version == _http11VersionLong)
|
||||
{
|
||||
knownVersion = HttpVersion.Http11;
|
||||
}
|
||||
else if (version == _http10VersionLong)
|
||||
{
|
||||
knownVersion = HttpVersion.Http10;
|
||||
}
|
||||
else
|
||||
{
|
||||
knownVersion = HttpVersion.Unknown;
|
||||
}
|
||||
|
||||
knownVersion = HttpVersion.Unknown;
|
||||
length = 0;
|
||||
return false;
|
||||
return knownVersion;
|
||||
}
|
||||
|
||||
public static string VersionToString(HttpVersion httpVersion)
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
|
||||
public bool ParseRequestLine<T>(T handler, ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined) where T : IHttpRequestLineHandler
|
||||
{
|
||||
handler.OnStartLine(HttpMethod.Get, HttpVersion.Http11, new Span<byte>(_target), new Span<byte>(_target), Span<byte>.Empty, Span<byte>.Empty);
|
||||
handler.OnStartLine(HttpMethod.Get, HttpVersion.Http11, new Span<byte>(_target), new Span<byte>(_target), Span<byte>.Empty, Span<byte>.Empty, false);
|
||||
|
||||
consumed = buffer.Start;
|
||||
examined = buffer.End;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
}
|
||||
}
|
||||
|
||||
public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod)
|
||||
public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,14 +49,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
It.IsAny<Span<byte>>(),
|
||||
It.IsAny<Span<byte>>(),
|
||||
It.IsAny<Span<byte>>(),
|
||||
It.IsAny<Span<byte>>()))
|
||||
.Callback<HttpMethod, HttpVersion, Span<byte>, Span<byte>, Span<byte>, Span<byte>>((method, version, target, path, query, customMethod) =>
|
||||
It.IsAny<Span<byte>>(),
|
||||
It.IsAny<bool>()))
|
||||
.Callback<HttpMethod, HttpVersion, Span<byte>, Span<byte>, Span<byte>, Span<byte>, bool>((method, version, target, path, query, customMethod, pathEncoded) =>
|
||||
{
|
||||
parsedMethod = method != HttpMethod.Custom ? HttpUtilities.MethodToString(method) : customMethod.GetAsciiStringNonNullCharacters();
|
||||
parsedVersion = HttpUtilities.VersionToString(version);
|
||||
parsedRawTarget = target.GetAsciiStringNonNullCharacters();
|
||||
parsedRawPath = path.GetAsciiStringNonNullCharacters();
|
||||
parsedQuery = query.GetAsciiStringNonNullCharacters();
|
||||
pathEncoded = false;
|
||||
});
|
||||
|
||||
Assert.True(parser.ParseRequestLine(requestLineHandler.Object, buffer, out var consumed, out var examined));
|
||||
|
|
|
|||
Loading…
Reference in New Issue