File: System\Web\Util\UriUtil.cs
Web Access
Project: src\src\libraries\System.Web.HttpUtility\src\System.Web.HttpUtility.csproj (System.Web.HttpUtility)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics.CodeAnalysis;
 
namespace System.Web.Util
{
    internal static class UriUtil
    {
        // Attempts to split a URI into its constituent pieces.
        // Even if this method returns true, one or more of the out parameters might contain a null or empty string, e.g. if there is no query / fragment.
        // Concatenating the pieces back together will form the original input string.
        internal static bool TrySplitUriForPathEncode(string input, out ReadOnlySpan<char> schemeAndAuthority, [NotNullWhen(true)] out string? path, out ReadOnlySpan<char> queryAndFragment)
        {
            // Strip off ?query and #fragment if they exist, since we're not going to look at them
            int queryFragmentSeparatorPos = input.AsSpan().IndexOfAny('?', '#'); // query fragment separators
            string inputWithoutQueryFragment;
            if (queryFragmentSeparatorPos >= 0)
            {
                inputWithoutQueryFragment = input.Substring(0, queryFragmentSeparatorPos);
                queryAndFragment = input.AsSpan(queryFragmentSeparatorPos);
            }
            else
            {
                // no query or fragment separator
                inputWithoutQueryFragment = input;
                queryAndFragment = ReadOnlySpan<char>.Empty;
            }
 
            // Use Uri class to parse the url into authority and path, use that to help decide
            // where to split the string. Do not rebuild the url from the Uri instance, as that
            // might have subtle changes from the original string (for example, see below about "://").
            if (Uri.TryCreate(inputWithoutQueryFragment, UriKind.Absolute, out Uri? uri))
            {
                string authority = uri.Authority; // e.g. "foo:81" in "http://foo:81/bar"
                if (!string.IsNullOrEmpty(authority))
                {
                    // don't make any assumptions about the scheme or the "://" part.
                    // For example, the "//" could be missing, or there could be "///" as in "file:///C:\foo.txt"
                    // To retain the same string as originally given, find the authority in the original url and include
                    // everything up to that.
                    int authorityIndex = inputWithoutQueryFragment.IndexOf(authority, StringComparison.OrdinalIgnoreCase);
                    if (authorityIndex >= 0)
                    {
                        int schemeAndAuthorityLength = authorityIndex + authority.Length;
                        schemeAndAuthority = input.AsSpan(0, schemeAndAuthorityLength);
                        path = inputWithoutQueryFragment.Substring(schemeAndAuthorityLength);
                        return true;
                    }
                }
            }
 
            // Not a safe URL
            schemeAndAuthority = ReadOnlySpan<char>.Empty;
            path = null;
            queryAndFragment = ReadOnlySpan<char>.Empty;
            return false;
        }
    }
}