< Summary

Information
Class: System.Net.Http.Headers.AuthenticationHeaderValue
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\Headers\AuthenticationHeaderValue.cs
Line coverage
72%
Covered lines: 118
Uncovered lines: 45
Coverable lines: 163
Total lines: 296
Line coverage: 72.3%
Branch coverage
78%
Covered branches: 54
Total branches: 69
Branch coverage: 78.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
.ctor(...)100%11100%
.ctor(...)100%110%
ToString()100%22100%
Equals(...)0%880%
GetHashCode()0%220%
Parse(...)100%110%
TryParse(...)0%220%
GetAuthenticationLength(...)90.32%3131100%
TrySkipFirstBlob(...)100%1010100%
TryGetParametersEndIndex(...)100%1414100%
System.ICloneable.Clone()100%110%

File(s)

D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\Headers\AuthenticationHeaderValue.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Diagnostics.CodeAnalysis;
 6
 7namespace System.Net.Http.Headers
 8{
 9    public class AuthenticationHeaderValue : ICloneable
 10    {
 11        private readonly string _scheme;
 12        private readonly string? _parameter;
 13
 14        public string Scheme
 15        {
 016            get { return _scheme; }
 17        }
 18
 19        // We simplify parameters by just considering them one string. The caller is responsible for correctly parsing
 20        // the string.
 21        // The reason is that we can't determine the format of parameters. According to Errata 1959 in RFC 2617
 22        // parameters can be "token", "quoted-string", or "#auth-param" where "auth-param" is defined as
 23        // "token "=" ( token | quoted-string )". E.g. take the following BASIC example:
 24        // Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
 25        // Due to Base64 encoding we have two final "=". The value is neither a token nor a quoted-string, so it must
 26        // be an auth-param according to the RFC definition. But that's also incorrect: auth-param means that we
 27        // consider the value before the first "=" as "name" and the final "=" as "value".
 28        public string? Parameter
 29        {
 030            get { return _parameter; }
 31        }
 32
 33        public AuthenticationHeaderValue(string scheme)
 9951834            : this(scheme, null)
 9951835        {
 9951836        }
 37
 10952538        public AuthenticationHeaderValue(string scheme, string? parameter)
 10952539        {
 10952540            HeaderUtilities.CheckValidToken(scheme);
 10952541            HttpHeaders.CheckContainsNewLineOrNull(parameter);
 10952542            _scheme = scheme;
 10952543            _parameter = parameter;
 10952544        }
 45
 046        private AuthenticationHeaderValue(AuthenticationHeaderValue source)
 047        {
 048            Debug.Assert(source != null);
 49
 050            _scheme = source._scheme;
 051            _parameter = source._parameter;
 052        }
 53
 54        public override string ToString()
 17894055        {
 17894056            if (string.IsNullOrEmpty(_parameter))
 16258857            {
 16258858                return _scheme;
 59            }
 1635260            return _scheme + " " + _parameter;
 17894061        }
 62
 63        public override bool Equals([NotNullWhen(true)] object? obj)
 064        {
 065            AuthenticationHeaderValue? other = obj as AuthenticationHeaderValue;
 66
 067            if (other == null)
 068            {
 069                return false;
 70            }
 71
 072            if (string.IsNullOrEmpty(_parameter) && string.IsNullOrEmpty(other._parameter))
 073            {
 074                return (string.Equals(_scheme, other._scheme, StringComparison.OrdinalIgnoreCase));
 75            }
 76            else
 077            {
 78                // Since we can't parse the parameter, we use case-sensitive comparison.
 079                return string.Equals(_scheme, other._scheme, StringComparison.OrdinalIgnoreCase) &&
 080                    string.Equals(_parameter, other._parameter, StringComparison.Ordinal);
 81            }
 082        }
 83
 84        public override int GetHashCode()
 085        {
 086            int result = StringComparer.OrdinalIgnoreCase.GetHashCode(_scheme);
 87
 088            if (!string.IsNullOrEmpty(_parameter))
 089            {
 090                result ^= _parameter.GetHashCode();
 091            }
 92
 093            return result;
 094        }
 95
 96        public static AuthenticationHeaderValue Parse(string input)
 097        {
 098            int index = 0;
 099            return (AuthenticationHeaderValue)GenericHeaderParser.SingleValueAuthenticationParser.ParseValue(
 0100                input, null, ref index);
 0101        }
 102
 103        public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true)] out AuthenticationHeaderValue
 0104        {
 0105            int index = 0;
 0106            parsedValue = null;
 107
 0108            if (GenericHeaderParser.SingleValueAuthenticationParser.TryParseValue(input, null, ref index, out object? ou
 0109            {
 0110                parsedValue = (AuthenticationHeaderValue)output!;
 0111                return true;
 112            }
 0113            return false;
 0114        }
 115
 116        internal static int GetAuthenticationLength(string? input, int startIndex, out object? parsedValue)
 113839117        {
 113839118            Debug.Assert(startIndex >= 0);
 119
 113839120            parsedValue = null;
 121
 113839122            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length) || HttpRuleParser.ContainsNewLineOrNull(inpu
 4034123            {
 4034124                return 0;
 125            }
 126
 127            // Parse the scheme string: <scheme> in '<scheme> <parameter>'
 109805128            int schemeLength = HttpRuleParser.GetTokenLength(input, startIndex);
 129
 109805130            if (schemeLength == 0)
 58131            {
 58132                return 0;
 133            }
 134
 109747135            string? targetScheme = null;
 109747136            switch (schemeLength)
 137            {
 138                // Avoid allocating a scheme string for the most common cases.
 75464139                case 5: targetScheme = "Basic"; break;
 20130140                case 6: targetScheme = "Digest"; break;
 12302141                case 4: targetScheme = "NTLM"; break;
 21678142                case 9: targetScheme = "Negotiate"; break;
 143            }
 144
 109747145            string scheme = targetScheme != null && string.CompareOrdinal(input, startIndex, targetScheme, 0, schemeLeng
 109747146                targetScheme :
 109747147                input.Substring(startIndex, schemeLength);
 148
 109747149            int current = startIndex + schemeLength;
 109747150            int whitespaceLength = HttpRuleParser.GetWhitespaceLength(input, current);
 109747151            current += whitespaceLength;
 152
 109747153            if ((current == input.Length) || (input[current] == ','))
 99518154            {
 155                // If we only have a scheme followed by whitespace, we're done.
 99518156                parsedValue = new AuthenticationHeaderValue(scheme);
 99518157                return current - startIndex;
 158            }
 159
 160            // We need at least one space between the scheme and parameters. If there is no whitespace, then we must
 161            // have reached the end of the string (i.e. scheme-only string).
 10229162            if (whitespaceLength == 0)
 146163            {
 146164                return 0;
 165            }
 166
 167            // If we get here, we have a <scheme> followed by a whitespace. Now we expect the following:
 168            // '<scheme> <blob>[,<name>=<value>]*[, <otherscheme>...]*': <blob> potentially contains one
 169            // or more '=' characters, optionally followed by additional name/value pairs, optionally followed by
 170            // other schemes. <blob> may be a quoted string.
 171            // We look at the value after ',': if it is <token>=<value> then we have a parameter for <scheme>.
 172            // If we have either a <token>-only or <token><whitespace><blob> then we have another scheme.
 10083173            int parameterStartIndex = current;
 10083174            int parameterEndIndex = current;
 10083175            if (!TrySkipFirstBlob(input, ref current, ref parameterEndIndex))
 18176            {
 18177                return 0;
 178            }
 179
 10065180            if (current < input.Length)
 8705181            {
 8705182                if (!TryGetParametersEndIndex(input, ref current, ref parameterEndIndex))
 58183                {
 58184                    return 0;
 185                }
 8647186            }
 187
 10007188            string parameter = input.Substring(parameterStartIndex, parameterEndIndex - parameterStartIndex + 1);
 10007189            parsedValue = new AuthenticationHeaderValue(scheme, parameter);
 10007190            return current - startIndex;
 113839191        }
 192
 193        private static bool TrySkipFirstBlob(string input, ref int current, ref int parameterEndIndex)
 10083194        {
 195            // Find the delimiter: Note that <blob> in "<scheme> <blob>" may be a token, quoted string, name/value
 196            // pair or a Base64 encoded string. So make sure that we don't consider ',' characters within a quoted
 197            // string as delimiter.
 92806198            while ((current < input.Length) && (input[current] != ','))
 82741199            {
 82741200                if (input[current] == '"')
 3034201                {
 202                    int quotedStringLength;
 3034203                    if (HttpRuleParser.GetQuotedStringLength(input, current, out quotedStringLength) !=
 3034204                        HttpParseResult.Parsed)
 18205                    {
 206                        // We have a quote but an invalid quoted-string.
 18207                        return false;
 208                    }
 3016209                    current += quotedStringLength;
 3016210                    parameterEndIndex = current - 1; // -1 because 'current' points to the char after the final '"'
 3016211                }
 212                else
 79707213                {
 79707214                    int whitespaceLength = HttpRuleParser.GetWhitespaceLength(input, current);
 215
 216                    // We don't want trailing whitespace to be considered part of the parameter blob. Increment
 217                    // 'parameterEndIndex' only if we don't have a whitespace. E.g. "Basic AbC=  , NTLM" should return
 218                    // "AbC=" as parameter ignoring the spaces before ','.
 79707219                    if (whitespaceLength == 0)
 68670220                    {
 68670221                        parameterEndIndex = current;
 68670222                        current++;
 68670223                    }
 224                    else
 11037225                    {
 11037226                        current += whitespaceLength;
 11037227                    }
 79707228                }
 82723229            }
 230
 10065231            return true;
 10083232        }
 233
 234        private static bool TryGetParametersEndIndex(string input, ref int parseEndIndex, ref int parameterEndIndex)
 8705235        {
 8705236            Debug.Assert(parseEndIndex < input.Length, "Expected string to have at least 1 char");
 8705237            Debug.Assert(input[parseEndIndex] == ',');
 238
 8705239            int current = parseEndIndex;
 240            do
 9518241            {
 9518242                current++; // skip ',' delimiter
 243
 9518244                current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, current, true, out _);
 9518245                if (current == input.Length)
 300246                {
 300247                    return true;
 248                }
 249
 250                // Now we have to determine if after ',' we have a list of <name>=<value> pairs that are part of
 251                // the auth scheme parameters OR if we have another auth scheme. Either way, after ',' we expect a
 252                // valid token that is either the <name> in a <name>=<value> pair OR <scheme> of another scheme.
 9218253                int tokenLength = HttpRuleParser.GetTokenLength(input, current);
 9218254                if (tokenLength == 0)
 28255                {
 28256                    return false;
 257                }
 258
 9190259                current += tokenLength;
 9190260                current += HttpRuleParser.GetWhitespaceLength(input, current);
 261
 262                // If we reached the end of the string or the token is followed by anything but '=', then the parsed
 263                // token is another scheme name. The string representing parameters ends before the token (e.g.
 264                // "Digest a=b, c=d, NTLM": return scheme "Digest" with parameters string "a=b, c=d").
 9190265                if ((current == input.Length) || (input[current] != '='))
 8240266                {
 8240267                    return true;
 268                }
 269
 950270                current++; // skip '=' delimiter
 950271                current += HttpRuleParser.GetWhitespaceLength(input, current);
 950272                int valueLength = NameValueHeaderValue.GetValueLength(input, current);
 273
 274                // After '<name>=' we expect a valid <value> (either token or quoted string)
 950275                if (valueLength == 0)
 30276                {
 30277                    return false;
 278                }
 279
 280                // Update parameter end index, since we just parsed a valid <name>=<value> pair that is part of the
 281                // parameters string.
 920282                current += valueLength;
 920283                parameterEndIndex = current - 1; // -1 because 'current' already points to the char after <value>
 920284                current += HttpRuleParser.GetWhitespaceLength(input, current);
 920285                parseEndIndex = current; // this essentially points to parameterEndIndex + whitespace + next char
 1840286            } while ((current < input.Length) && (input[current] == ','));
 287
 107288            return true;
 8705289        }
 290
 291        object ICloneable.Clone()
 0292        {
 0293            return new AuthenticationHeaderValue(this);
 0294        }
 295    }
 296}