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();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
|
@ -193,9 +193,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
// check and skip it
|
||||
if (rawUrlInBytes.Length > 0 && rawUrlInBytes[rawUrlInBytes.Length - 1] == 0)
|
||||
{
|
||||
var newRawUrlInBytes = new byte[rawUrlInBytes.Length - 1];
|
||||
Array.Copy(rawUrlInBytes, newRawUrlInBytes, newRawUrlInBytes.Length);
|
||||
rawUrlInBytes = newRawUrlInBytes;
|
||||
rawUrlInBytes = rawUrlInBytes.Slice(0, rawUrlInBytes.Length - 1);
|
||||
}
|
||||
|
||||
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(rawUrlInBytes);
|
||||
|
|
|
|||
|
|
@ -138,17 +138,14 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
return null;
|
||||
}
|
||||
|
||||
internal byte[] GetRawUrlInBytes()
|
||||
internal Span<byte> GetRawUrlInBytes()
|
||||
{
|
||||
if (NativeRequest->pRawUrl != null && NativeRequest->RawUrlLength > 0)
|
||||
{
|
||||
var result = new byte[NativeRequest->RawUrlLength];
|
||||
Marshal.Copy((IntPtr)NativeRequest->pRawUrl, result, 0, NativeRequest->RawUrlLength);
|
||||
|
||||
return result;
|
||||
return new Span<byte>(NativeRequest->pRawUrl, NativeRequest->RawUrlLength);
|
||||
}
|
||||
|
||||
return null;
|
||||
return default;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
using System;
|
||||
|
|
@ -8,12 +8,10 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
{
|
||||
internal static class RawUrlHelper
|
||||
{
|
||||
private static readonly byte[] _forwardSlashPath = Encoding.ASCII.GetBytes("/");
|
||||
|
||||
/// <summary>
|
||||
/// Find the segment of the URI byte array which represents the path.
|
||||
/// </summary>
|
||||
public static ArraySegment<byte> GetPath(byte[] raw)
|
||||
public static Span<byte> GetPath(Span<byte> raw)
|
||||
{
|
||||
// performance
|
||||
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 '/'
|
||||
// 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.
|
||||
pathStartIndex = Find(raw, authorityStartIndex, '/');
|
||||
pathStartIndex = Find(raw, authorityStartIndex, (byte)'/');
|
||||
if (pathStartIndex == -1)
|
||||
{
|
||||
// e.g. for request lines like: 'GET http://myserver' (no final '/')
|
||||
// At this point we can return a path with a slash.
|
||||
return new ArraySegment<byte>(_forwardSlashPath);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -67,7 +65,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
scan++;
|
||||
}
|
||||
|
||||
return new ArraySegment<byte>(raw, pathStartIndex, scan - pathStartIndex);
|
||||
return raw.Slice(pathStartIndex, scan - pathStartIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -75,7 +73,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
/// </summary>
|
||||
/// <param name="raw">The byte array represents the raw URI</param>
|
||||
/// <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)
|
||||
{
|
||||
|
|
@ -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;
|
||||
}
|
||||
return idx + begin;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||
|
|
@ -17,23 +18,19 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
encoderShouldEmitUTF8Identifier: false,
|
||||
throwOnInvalidBytes: true);
|
||||
|
||||
public static string DecodeAndUnescapePath(byte[] rawUrlBytes)
|
||||
public static string DecodeAndUnescapePath(Span<byte> rawUrlBytes)
|
||||
{
|
||||
if (rawUrlBytes == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(rawUrlBytes));
|
||||
}
|
||||
|
||||
if (rawUrlBytes.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Length of the URL cannot be zero.", nameof(rawUrlBytes));
|
||||
}
|
||||
|
||||
Debug.Assert(rawUrlBytes.Length == 0, "Length of the URL cannot be zero.");
|
||||
var rawPath = RawUrlHelper.GetPath(rawUrlBytes);
|
||||
|
||||
if (rawPath.Length == 0)
|
||||
{
|
||||
return "/";
|
||||
}
|
||||
|
||||
var unescapedPath = Unescape(rawPath);
|
||||
|
||||
return UTF8.GetString(unescapedPath.Array, unescapedPath.Offset, unescapedPath.Count);
|
||||
return UTF8.GetString(unescapedPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -41,19 +38,16 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
/// </summary>
|
||||
/// <param name="rawPath">The raw path string to be unescaped</param>
|
||||
/// <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
|
||||
var reader = rawPath.Offset;
|
||||
var reader = 0;
|
||||
|
||||
// the slot to write the unescaped byte
|
||||
var writer = rawPath.Offset;
|
||||
var writer = 0;
|
||||
|
||||
// the end of the path
|
||||
var end = rawPath.Offset + rawPath.Count;
|
||||
|
||||
// the byte array
|
||||
var buffer = rawPath.Array;
|
||||
var end = rawPath.Length;
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
|
@ -62,7 +56,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
break;
|
||||
}
|
||||
|
||||
if (rawPath.Array[reader] == '%')
|
||||
if (rawPath[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
|
||||
// be scanned after the process. A failed decoding means the chars
|
||||
// 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;
|
||||
}
|
||||
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>
|
||||
|
|
@ -96,7 +90,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
/// <param name="writer">The place to write to</param>
|
||||
/// <param name="end">The end of the buffer</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,
|
||||
// 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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -260,7 +254,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
/// <param name="end">The end of the buffer</param>
|
||||
/// <param name="buffer">The byte array</param>
|
||||
/// <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++] != '%')
|
||||
{
|
||||
|
|
@ -300,7 +294,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
|||
/// <param name="end">The end of the buffer</param>
|
||||
/// <param name="buffer">The byte array</param>
|
||||
/// <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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue