File: System\Net\Http\HttpMethod.cs
Web Access
Project: src\src\libraries\System.Net.Http\src\System.Net.Http.csproj (System.Net.Http)
// 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;
using System.Net.Http.QPack;
 
namespace System.Net.Http
{
    public partial class HttpMethod : IEquatable<HttpMethod>
    {
        private readonly string _method;
        private int _hashcode;
 
        public static HttpMethod Get { get; } = new("GET", H3StaticTable.MethodGet);
        public static HttpMethod Put { get; } = new("PUT", H3StaticTable.MethodPut);
        public static HttpMethod Post { get; } = new("POST", H3StaticTable.MethodPost);
        public static HttpMethod Delete { get; } = new("DELETE", H3StaticTable.MethodDelete);
        public static HttpMethod Head { get; } = new("HEAD", H3StaticTable.MethodHead);
        public static HttpMethod Options { get; } = new("OPTIONS", H3StaticTable.MethodOptions);
        public static HttpMethod Trace { get; } = new("TRACE", http3StaticTableIndex: -1);
        public static HttpMethod Patch { get; } = new("PATCH", http3StaticTableIndex: -1);
 
        /// <summary>Gets the HTTP CONNECT protocol method.</summary>
        /// <value>The HTTP CONNECT method.</value>
        public static HttpMethod Connect { get; } = new("CONNECT", H3StaticTable.MethodConnect);
 
        public string Method => _method;
 
        public HttpMethod(string method)
        {
            ArgumentException.ThrowIfNullOrWhiteSpace(method);
            if (!HttpRuleParser.IsToken(method))
            {
                throw new FormatException(SR.net_http_httpmethod_format_error);
            }
 
            _method = method;
            Initialize(method);
        }
 
        private HttpMethod(string method, int http3StaticTableIndex)
        {
            _method = method;
            Initialize(http3StaticTableIndex);
        }
 
        // SocketsHttpHandler-specific implementation has extra init logic.
        partial void Initialize(int http3Index);
        partial void Initialize(string method);
 
        public bool Equals([NotNullWhen(true)] HttpMethod? other) =>
            other is not null &&
            string.Equals(_method, other._method, StringComparison.OrdinalIgnoreCase);
 
        public override bool Equals([NotNullWhen(true)] object? obj) =>
            obj is HttpMethod method &&
            Equals(method);
 
        public override int GetHashCode()
        {
            if (_hashcode == 0)
            {
                _hashcode = StringComparer.OrdinalIgnoreCase.GetHashCode(_method);
            }
 
            return _hashcode;
        }
 
        public override string ToString() => _method;
 
        public static bool operator ==(HttpMethod? left, HttpMethod? right) =>
            left is null || right is null
                ? ReferenceEquals(left, right)
                : left.Equals(right);
 
        public static bool operator !=(HttpMethod? left, HttpMethod? right) =>
            !(left == right);
 
        /// <summary>Parses the provided <paramref name="method"/> into an <see cref="HttpMethod"/> instance.</summary>
        /// <param name="method">The method to parse.</param>
        /// <returns>An <see cref="HttpMethod"/> instance for the provided <paramref name="method"/>.</returns>
        /// <remarks>
        /// This method may return a singleton instance for known methods; for example, it may return <see cref="Get"/>
        /// if "GET" is specified. The parsing is performed in a case-insensitive manner, so it may also return <see cref="Get"/>
        /// if "get" is specified. For unknown methods, a new <see cref="HttpMethod"/> instance is returned, with the
        /// same validation being performed as by the <see cref="HttpMethod(string)"/> constructor.
        /// </remarks>
        public static HttpMethod Parse(ReadOnlySpan<char> method) =>
            GetKnownMethod(method) ??
            new HttpMethod(method.ToString());
 
        internal static HttpMethod? GetKnownMethod(ReadOnlySpan<char> method)
        {
            if (method.Length >= 3) // 3 == smallest known method
            {
                HttpMethod? match = (method[0] | 0x20) switch
                {
                    'c' => Connect,
                    'd' => Delete,
                    'g' => Get,
                    'h' => Head,
                    'o' => Options,
                    'p' => method.Length switch
                    {
                        3 => Put,
                        4 => Post,
                        _ => Patch,
                    },
                    't' => Trace,
                    _ => null,
                };
 
                if (match is not null &&
                    method.Equals(match._method, StringComparison.OrdinalIgnoreCase))
                {
                    return match;
                }
            }
 
            return null;
        }
    }
}