File: System\Web\HttpUtility.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.
 
// Authors:
//   Patrik Torstensson (Patrik.Torstensson@labs2.com)
//   Wictor Wil\u00E9n (decode/encode functions) (wictor@ibizkit.se)
//   Tim Coleman (tim@timcoleman.com)
//   Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
 
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
using System.Web.Util;
 
namespace System.Web
{
    public sealed class HttpUtility
    {
        private sealed class HttpQSCollection : NameValueCollection
        {
            internal HttpQSCollection()
                : base(StringComparer.OrdinalIgnoreCase)
            {
            }
 
            public override string ToString()
            {
                int count = Count;
                if (count == 0)
                {
                    return "";
                }
 
                StringBuilder sb = new StringBuilder();
                string?[] keys = AllKeys;
                for (int i = 0; i < count; i++)
                {
                    string? key = keys[i];
                    string[]? values = GetValues(key);
                    if (values != null)
                    {
                        foreach (string value in values)
                        {
                            if (!string.IsNullOrEmpty(key))
                            {
                                sb.Append(UrlEncode(key)).Append('=');
                            }
                            sb.Append(UrlEncode(value)).Append('&');
                        }
                    }
                }
 
                return sb.Length > 0 ? sb.ToString(0, sb.Length - 1) : "";
            }
        }
 
        public static NameValueCollection ParseQueryString(string query) => ParseQueryString(query, Encoding.UTF8);
 
        public static NameValueCollection ParseQueryString(string query, Encoding encoding)
        {
            ArgumentNullException.ThrowIfNull(query);
            ArgumentNullException.ThrowIfNull(encoding);
 
            HttpQSCollection result = new HttpQSCollection();
            int queryLength = query.Length;
            int namePos = query.StartsWith('?') ? 1 : 0;
            if (queryLength == namePos)
            {
                return result;
            }
 
            while (namePos <= queryLength)
            {
                int valuePos = -1, valueEnd = -1;
                for (int q = namePos; q < queryLength; q++)
                {
                    if (valuePos == -1 && query[q] == '=')
                    {
                        valuePos = q + 1;
                    }
                    else if (query[q] == '&')
                    {
                        valueEnd = q;
                        break;
                    }
                }
 
                string? name;
                if (valuePos == -1)
                {
                    name = null;
                    valuePos = namePos;
                }
                else
                {
                    name = UrlDecode(query.AsSpan(namePos, valuePos - namePos - 1), encoding);
                }
 
                if (valueEnd < 0)
                {
                    valueEnd = query.Length;
                }
 
                namePos = valueEnd + 1;
                string value = UrlDecode(query.AsSpan(valuePos, valueEnd - valuePos), encoding);
                result.Add(name, value);
            }
 
            return result;
        }
 
        [return: NotNullIfNotNull(nameof(s))]
        public static string? HtmlDecode(string? s) => HttpEncoder.HtmlDecode(s);
 
        public static void HtmlDecode(string? s, TextWriter output) => HttpEncoder.HtmlDecode(s, output);
 
        [return: NotNullIfNotNull(nameof(s))]
        public static string? HtmlEncode(string? s) => HttpEncoder.HtmlEncode(s);
 
        [return: NotNullIfNotNull(nameof(value))]
        public static string? HtmlEncode(object? value) =>
            value switch
            {
                null => null,
                IHtmlString ihs => ihs.ToHtmlString() ?? string.Empty,
                _ => HtmlEncode(Convert.ToString(value, CultureInfo.CurrentCulture) ?? string.Empty),
            };
 
        public static void HtmlEncode(string? s, TextWriter output) => HttpEncoder.HtmlEncode(s, output);
 
        [return: NotNullIfNotNull(nameof(s))]
        public static string? HtmlAttributeEncode(string? s) => HttpEncoder.HtmlAttributeEncode(s);
 
        public static void HtmlAttributeEncode(string? s, TextWriter output) => HttpEncoder.HtmlAttributeEncode(s, output);
 
        [return: NotNullIfNotNull(nameof(str))]
        public static string? UrlEncode(string? str) => UrlEncode(str, Encoding.UTF8);
 
        [return: NotNullIfNotNull(nameof(str))]
        public static string? UrlPathEncode(string? str) => HttpEncoder.UrlPathEncode(str);
 
        [return: NotNullIfNotNull(nameof(str))]
        public static string? UrlEncode(string? str, Encoding e) =>
            str == null ? null : Encoding.ASCII.GetString(UrlEncodeToBytes(str, e));
 
        [return: NotNullIfNotNull(nameof(bytes))]
        public static string? UrlEncode(byte[]? bytes) => bytes == null ? null : Encoding.ASCII.GetString(UrlEncodeToBytes(bytes));
 
        [return: NotNullIfNotNull(nameof(bytes))]
        public static string? UrlEncode(byte[]? bytes, int offset, int count) => bytes == null ? null : Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, offset, count));
 
        [return: NotNullIfNotNull(nameof(str))]
        public static byte[]? UrlEncodeToBytes(string? str) => UrlEncodeToBytes(str, Encoding.UTF8);
 
        [return: NotNullIfNotNull(nameof(bytes))]
        public static byte[]? UrlEncodeToBytes(byte[]? bytes) => bytes == null ? null : UrlEncodeToBytes(bytes, 0, bytes.Length);
 
        [Obsolete("This method produces non-standards-compliant output and has interoperability issues. The preferred alternative is UrlEncodeToBytes(String).")]
        [return: NotNullIfNotNull(nameof(str))]
        public static byte[]? UrlEncodeUnicodeToBytes(string? str) => str == null ? null : Encoding.ASCII.GetBytes(UrlEncodeUnicode(str));
 
        [return: NotNullIfNotNull(nameof(str))]
        public static string? UrlDecode(string? str) => UrlDecode(str, Encoding.UTF8);
 
        [return: NotNullIfNotNull(nameof(bytes))]
        public static string? UrlDecode(byte[]? bytes, Encoding e) => bytes == null ? null : UrlDecode(bytes, 0, bytes.Length, e);
 
        [return: NotNullIfNotNull(nameof(str))]
        public static byte[]? UrlDecodeToBytes(string? str) => UrlDecodeToBytes(str, Encoding.UTF8);
 
        [return: NotNullIfNotNull(nameof(str))]
        public static byte[]? UrlDecodeToBytes(string? str, Encoding e)
        {
            const int StackallocThreshold = 512;
            if (str == null)
            {
                return null;
            }
 
            if (e.GetMaxByteCount(str.Length) <= StackallocThreshold)
            {
                Span<byte> bytes = stackalloc byte[StackallocThreshold];
                int encodedBytes = e.GetBytes(str, bytes);
 
                return HttpEncoder.UrlDecode(bytes.Slice(0, encodedBytes));
            }
 
            return UrlDecodeToBytes(e.GetBytes(str));
        }
 
        [return: NotNullIfNotNull(nameof(bytes))]
        public static byte[]? UrlDecodeToBytes(byte[]? bytes) => bytes == null ? null : HttpEncoder.UrlDecode(bytes);
 
        [return: NotNullIfNotNull(nameof(str))]
        public static byte[]? UrlEncodeToBytes(string? str, Encoding e) => str == null ? null : HttpEncoder.UrlEncode(str, e);
 
        [return: NotNullIfNotNull(nameof(bytes))]
        public static byte[]? UrlEncodeToBytes(byte[]? bytes, int offset, int count) => HttpEncoder.UrlEncode(bytes, offset, count);
 
        [Obsolete("This method produces non-standards-compliant output and has interoperability issues. The preferred alternative is UrlEncode(String).")]
        [return: NotNullIfNotNull(nameof(str))]
        public static string? UrlEncodeUnicode(string? str) => HttpEncoder.UrlEncodeUnicode(str);
 
        [return: NotNullIfNotNull(nameof(str))]
        public static string? UrlDecode(string? str, Encoding e) => HttpEncoder.UrlDecode(str, e);
 
        private static string UrlDecode(ReadOnlySpan<char> str, Encoding e) => HttpEncoder.UrlDecode(str, e);
 
        [return: NotNullIfNotNull(nameof(bytes))]
        public static string? UrlDecode(byte[]? bytes, int offset, int count, Encoding e) =>
            HttpEncoder.UrlDecode(bytes, offset, count, e);
 
        [return: NotNullIfNotNull(nameof(bytes))]
        public static byte[]? UrlDecodeToBytes(byte[]? bytes, int offset, int count) => HttpEncoder.UrlDecode(bytes, offset, count);
 
        public static string JavaScriptStringEncode(string? value) => HttpEncoder.JavaScriptStringEncode(value, false);
 
        public static string JavaScriptStringEncode(string? value, bool addDoubleQuotes) => HttpEncoder.JavaScriptStringEncode(value, addDoubleQuotes);
    }
}