Spanify path -> string logic (#9628)
- Use spans instead of byte[] and array segments when turning the URL into a string
This commit is contained in:
parent
c745585d4c
commit
5f3bdba584
|
|
@ -184,7 +184,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
var rawUrlInBytes = GetRawUrlInBytes();
|
var rawUrlInBytes = GetRawUrlInBytes();
|
||||||
|
|
||||||
// Pre Windows 10 RS2 applicationInitialization request might not have pRawUrl set, fallback to cocked url
|
// Pre Windows 10 RS2 applicationInitialization request might not have pRawUrl set, fallback to cocked url
|
||||||
if (rawUrlInBytes == null)
|
if (rawUrlInBytes.Length == 0)
|
||||||
{
|
{
|
||||||
return GetCookedUrl().GetAbsPath();
|
return GetCookedUrl().GetAbsPath();
|
||||||
}
|
}
|
||||||
|
|
@ -193,9 +193,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
// check and skip it
|
// check and skip it
|
||||||
if (rawUrlInBytes.Length > 0 && rawUrlInBytes[rawUrlInBytes.Length - 1] == 0)
|
if (rawUrlInBytes.Length > 0 && rawUrlInBytes[rawUrlInBytes.Length - 1] == 0)
|
||||||
{
|
{
|
||||||
var newRawUrlInBytes = new byte[rawUrlInBytes.Length - 1];
|
rawUrlInBytes = rawUrlInBytes.Slice(0, rawUrlInBytes.Length - 1);
|
||||||
Array.Copy(rawUrlInBytes, newRawUrlInBytes, newRawUrlInBytes.Length);
|
|
||||||
rawUrlInBytes = newRawUrlInBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(rawUrlInBytes);
|
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(rawUrlInBytes);
|
||||||
|
|
|
||||||
|
|
@ -138,17 +138,14 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal byte[] GetRawUrlInBytes()
|
internal Span<byte> GetRawUrlInBytes()
|
||||||
{
|
{
|
||||||
if (NativeRequest->pRawUrl != null && NativeRequest->RawUrlLength > 0)
|
if (NativeRequest->pRawUrl != null && NativeRequest->RawUrlLength > 0)
|
||||||
{
|
{
|
||||||
var result = new byte[NativeRequest->RawUrlLength];
|
return new Span<byte>(NativeRequest->pRawUrl, NativeRequest->RawUrlLength);
|
||||||
Marshal.Copy((IntPtr)NativeRequest->pRawUrl, result, 0, NativeRequest->RawUrlLength);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal CookedUrl GetCookedUrl()
|
internal CookedUrl GetCookedUrl()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -8,12 +8,10 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
{
|
{
|
||||||
internal static class RawUrlHelper
|
internal static class RawUrlHelper
|
||||||
{
|
{
|
||||||
private static readonly byte[] _forwardSlashPath = Encoding.ASCII.GetBytes("/");
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Find the segment of the URI byte array which represents the path.
|
/// Find the segment of the URI byte array which represents the path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ArraySegment<byte> GetPath(byte[] raw)
|
public static Span<byte> GetPath(Span<byte> raw)
|
||||||
{
|
{
|
||||||
// performance
|
// performance
|
||||||
var pathStartIndex = 0;
|
var pathStartIndex = 0;
|
||||||
|
|
@ -38,12 +36,12 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
// and http.sys behavior: If the Uri contains a query, there must be at least one '/'
|
// and http.sys behavior: If the Uri contains a query, there must be at least one '/'
|
||||||
// between the authority and the '?' character: It's safe to just look for the first
|
// between the authority and the '?' character: It's safe to just look for the first
|
||||||
// '/' after the authority to determine the beginning of the path.
|
// '/' after the authority to determine the beginning of the path.
|
||||||
pathStartIndex = Find(raw, authorityStartIndex, '/');
|
pathStartIndex = Find(raw, authorityStartIndex, (byte)'/');
|
||||||
if (pathStartIndex == -1)
|
if (pathStartIndex == -1)
|
||||||
{
|
{
|
||||||
// e.g. for request lines like: 'GET http://myserver' (no final '/')
|
// e.g. for request lines like: 'GET http://myserver' (no final '/')
|
||||||
// At this point we can return a path with a slash.
|
// At this point we can return a path with a slash.
|
||||||
return new ArraySegment<byte>(_forwardSlashPath);
|
return default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -67,7 +65,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
scan++;
|
scan++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ArraySegment<byte>(raw, pathStartIndex, scan - pathStartIndex);
|
return raw.Slice(pathStartIndex, scan - pathStartIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -75,7 +73,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="raw">The byte array represents the raw URI</param>
|
/// <param name="raw">The byte array represents the raw URI</param>
|
||||||
/// <returns>Length of the matched bytes, 0 if it is not matched.</returns>
|
/// <returns>Length of the matched bytes, 0 if it is not matched.</returns>
|
||||||
private static int FindHttpOrHttps(byte[] raw)
|
private static int FindHttpOrHttps(Span<byte> raw)
|
||||||
{
|
{
|
||||||
if (raw.Length < 7)
|
if (raw.Length < 7)
|
||||||
{
|
{
|
||||||
|
|
@ -135,17 +133,14 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Find(byte[] raw, int begin, char target)
|
private static int Find(Span<byte> raw, int begin, byte target)
|
||||||
{
|
{
|
||||||
for (var idx = begin; idx < raw.Length; ++idx)
|
var idx = raw.Slice(begin).IndexOf(target);
|
||||||
|
if (idx != -1)
|
||||||
{
|
{
|
||||||
if (raw[idx] == target)
|
return idx + begin;
|
||||||
{
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return idx;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.HttpSys.Internal
|
namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
|
|
@ -17,23 +18,19 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
encoderShouldEmitUTF8Identifier: false,
|
encoderShouldEmitUTF8Identifier: false,
|
||||||
throwOnInvalidBytes: true);
|
throwOnInvalidBytes: true);
|
||||||
|
|
||||||
public static string DecodeAndUnescapePath(byte[] rawUrlBytes)
|
public static string DecodeAndUnescapePath(Span<byte> rawUrlBytes)
|
||||||
{
|
{
|
||||||
if (rawUrlBytes == null)
|
Debug.Assert(rawUrlBytes.Length == 0, "Length of the URL cannot be zero.");
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(rawUrlBytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawUrlBytes.Length == 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Length of the URL cannot be zero.", nameof(rawUrlBytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawPath = RawUrlHelper.GetPath(rawUrlBytes);
|
var rawPath = RawUrlHelper.GetPath(rawUrlBytes);
|
||||||
|
|
||||||
|
if (rawPath.Length == 0)
|
||||||
|
{
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
|
||||||
var unescapedPath = Unescape(rawPath);
|
var unescapedPath = Unescape(rawPath);
|
||||||
|
|
||||||
return UTF8.GetString(unescapedPath.Array, unescapedPath.Offset, unescapedPath.Count);
|
return UTF8.GetString(unescapedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -41,19 +38,16 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rawPath">The raw path string to be unescaped</param>
|
/// <param name="rawPath">The raw path string to be unescaped</param>
|
||||||
/// <returns>The unescaped path string</returns>
|
/// <returns>The unescaped path string</returns>
|
||||||
private static ArraySegment<byte> Unescape(ArraySegment<byte> rawPath)
|
private static ReadOnlySpan<byte> Unescape(Span<byte> rawPath)
|
||||||
{
|
{
|
||||||
// the slot to read the input
|
// the slot to read the input
|
||||||
var reader = rawPath.Offset;
|
var reader = 0;
|
||||||
|
|
||||||
// the slot to write the unescaped byte
|
// the slot to write the unescaped byte
|
||||||
var writer = rawPath.Offset;
|
var writer = 0;
|
||||||
|
|
||||||
// the end of the path
|
// the end of the path
|
||||||
var end = rawPath.Offset + rawPath.Count;
|
var end = rawPath.Length;
|
||||||
|
|
||||||
// the byte array
|
|
||||||
var buffer = rawPath.Array;
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
|
@ -62,7 +56,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawPath.Array[reader] == '%')
|
if (rawPath[reader] == '%')
|
||||||
{
|
{
|
||||||
var decodeReader = reader;
|
var decodeReader = reader;
|
||||||
|
|
||||||
|
|
@ -73,20 +67,20 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
// The decodeReader iterator is always moved to the first byte not yet
|
// The decodeReader iterator is always moved to the first byte not yet
|
||||||
// be scanned after the process. A failed decoding means the chars
|
// be scanned after the process. A failed decoding means the chars
|
||||||
// between the reader and decodeReader can be copied to output untouched.
|
// between the reader and decodeReader can be copied to output untouched.
|
||||||
if (!DecodeCore(ref decodeReader, ref writer, end, buffer))
|
if (!DecodeCore(ref decodeReader, ref writer, end, rawPath))
|
||||||
{
|
{
|
||||||
Copy(reader, decodeReader, ref writer, buffer);
|
Copy(reader, decodeReader, ref writer, rawPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
reader = decodeReader;
|
reader = decodeReader;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buffer[writer++] = buffer[reader++];
|
rawPath[writer++] = rawPath[reader++];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ArraySegment<byte>(buffer, rawPath.Offset, writer - rawPath.Offset);
|
return rawPath.Slice(0, writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -96,7 +90,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
/// <param name="writer">The place to write to</param>
|
/// <param name="writer">The place to write to</param>
|
||||||
/// <param name="end">The end of the buffer</param>
|
/// <param name="end">The end of the buffer</param>
|
||||||
/// <param name="buffer">The byte array</param>
|
/// <param name="buffer">The byte array</param>
|
||||||
private static bool DecodeCore(ref int reader, ref int writer, int end, byte[] buffer)
|
private static bool DecodeCore(ref int reader, ref int writer, int end, Span<byte> buffer)
|
||||||
{
|
{
|
||||||
// preserves the original head. if the percent-encodings cannot be interpreted as sequence of UTF-8 octets,
|
// preserves the original head. if the percent-encodings cannot be interpreted as sequence of UTF-8 octets,
|
||||||
// bytes from this till the last scanned one will be copied to the memory pointed by writer.
|
// bytes from this till the last scanned one will be copied to the memory pointed by writer.
|
||||||
|
|
@ -232,7 +226,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Copy(int begin, int end, ref int writer, byte[] buffer)
|
private static void Copy(int begin, int end, ref int writer, Span<byte> buffer)
|
||||||
{
|
{
|
||||||
while (begin != end)
|
while (begin != end)
|
||||||
{
|
{
|
||||||
|
|
@ -260,7 +254,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
/// <param name="end">The end of the buffer</param>
|
/// <param name="end">The end of the buffer</param>
|
||||||
/// <param name="buffer">The byte array</param>
|
/// <param name="buffer">The byte array</param>
|
||||||
/// <returns>The unescaped byte if success. Otherwise return -1.</returns>
|
/// <returns>The unescaped byte if success. Otherwise return -1.</returns>
|
||||||
private static int? UnescapePercentEncoding(ref int scan, int end, byte[] buffer)
|
private static int? UnescapePercentEncoding(ref int scan, int end, ReadOnlySpan<byte> buffer)
|
||||||
{
|
{
|
||||||
if (buffer[scan++] != '%')
|
if (buffer[scan++] != '%')
|
||||||
{
|
{
|
||||||
|
|
@ -300,7 +294,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
/// <param name="end">The end of the buffer</param>
|
/// <param name="end">The end of the buffer</param>
|
||||||
/// <param name="buffer">The byte array</param>
|
/// <param name="buffer">The byte array</param>
|
||||||
/// <returns>The hexadecimal value if successes, otherwise -1.</returns>
|
/// <returns>The hexadecimal value if successes, otherwise -1.</returns>
|
||||||
private static int? ReadHex(ref int scan, int end, byte[] buffer)
|
private static int? ReadHex(ref int scan, int end, ReadOnlySpan<byte> buffer)
|
||||||
{
|
{
|
||||||
if (scan == end)
|
if (scan == end)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue