File: System\Net\Security\SslApplicationProtocol.cs
Web Access
Project: src\src\libraries\System.Net.Security\src\System.Net.Security.csproj (System.Net.Security)
// 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;
using System.Diagnostics.CodeAnalysis;
using System.Text;
 
namespace System.Net.Security
{
    public readonly struct SslApplicationProtocol : IEquatable<SslApplicationProtocol>
    {
        private static readonly Encoding s_utf8 = Encoding.GetEncoding(Encoding.UTF8.CodePage, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback);
        private static readonly byte[] s_http3Utf8 = "h3"u8.ToArray();
        private static readonly byte[] s_http2Utf8 = "h2"u8.ToArray();
        private static readonly byte[] s_http11Utf8 = "http/1.1"u8.ToArray();
 
        // Refer to IANA on ApplicationProtocols: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
        /// <summary>Defines a <see cref="SslApplicationProtocol"/> instance for HTTP 3.0.</summary>
        public static readonly SslApplicationProtocol Http3 = new SslApplicationProtocol(s_http3Utf8, copy: false);
        /// <summary>Defines a <see cref="SslApplicationProtocol"/> instance for HTTP 2.0.</summary>
        public static readonly SslApplicationProtocol Http2 = new SslApplicationProtocol(s_http2Utf8, copy: false);
        /// <summary>Defines a <see cref="SslApplicationProtocol"/> instance for HTTP 1.1.</summary>
        public static readonly SslApplicationProtocol Http11 = new SslApplicationProtocol(s_http11Utf8, copy: false);
 
        private readonly byte[] _readOnlyProtocol;
 
        internal SslApplicationProtocol(byte[] protocol, bool copy)
        {
            Debug.Assert(protocol != null);
 
            // RFC 7301 states protocol size <= 255 bytes.
            if (protocol.Length == 0 || protocol.Length > 255)
            {
                throw new ArgumentException(SR.net_ssl_app_protocol_invalid, nameof(protocol));
            }
 
            _readOnlyProtocol = copy ?
                protocol.AsSpan().ToArray() :
                protocol;
        }
 
        public SslApplicationProtocol(byte[] protocol) :
            this(protocol ?? throw new ArgumentNullException(nameof(protocol)), copy: true)
        {
        }
 
        public SslApplicationProtocol(string protocol) :
            this(s_utf8.GetBytes(protocol ?? throw new ArgumentNullException(nameof(protocol))), copy: false)
        {
        }
 
        public ReadOnlyMemory<byte> Protocol => _readOnlyProtocol;
 
        public bool Equals(SslApplicationProtocol other) =>
            ((ReadOnlySpan<byte>)_readOnlyProtocol).SequenceEqual(other._readOnlyProtocol);
 
        public override bool Equals([NotNullWhen(true)] object? obj) => obj is SslApplicationProtocol protocol && Equals(protocol);
 
        public override int GetHashCode()
        {
            byte[] arr = _readOnlyProtocol;
            if (arr == null)
            {
                return 0;
            }
 
            int hash = 0;
            for (int i = 0; i < arr.Length; i++)
            {
                hash = ((hash << 5) + hash) ^ arr[i];
            }
 
            return hash;
        }
 
        public override string ToString()
        {
            byte[] arr = _readOnlyProtocol;
            try
            {
                return
                    arr is null ? string.Empty :
                    ReferenceEquals(arr, s_http3Utf8) ? "h3" :
                    ReferenceEquals(arr, s_http2Utf8) ? "h2" :
                    ReferenceEquals(arr, s_http11Utf8) ? "http/1.1" :
                    s_utf8.GetString(arr);
            }
            catch
            {
                // In case of decoding errors, return the byte values as hex string.
                char[] byteChars = new char[arr.Length * 5];
                int index = 0;
 
                for (int i = 0; i < byteChars.Length; i += 5)
                {
                    byte b = arr[index++];
                    byteChars[i] = '0';
                    byteChars[i + 1] = 'x';
                    byteChars[i + 2] = HexConverter.ToCharLower(b >> 4);
                    byteChars[i + 3] = HexConverter.ToCharLower(b);
                    byteChars[i + 4] = ' ';
                }
 
                return new string(byteChars, 0, byteChars.Length - 1);
            }
        }
 
        public static bool operator ==(SslApplicationProtocol left, SslApplicationProtocol right) =>
            left.Equals(right);
 
        public static bool operator !=(SslApplicationProtocol left, SslApplicationProtocol right) =>
            !(left == right);
    }
}