< Summary

Information
Class: System.Net.Http.Headers.NameValueHeaderValue
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\Headers\NameValueHeaderValue.cs
Line coverage
50%
Covered lines: 117
Uncovered lines: 117
Coverable lines: 234
Total lines: 394
Line coverage: 50%
Branch coverage
44%
Covered branches: 41
Total branches: 92
Branch coverage: 44.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor()100%11100%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)100%110%
GetHashCode()0%440%
Equals(...)0%880%
Parse(...)100%110%
TryParse(...)0%220%
ToString()100%22100%
AddToStringBuilder(...)75%4478.57%
ToString(...)100%1010100%
GetHashCode(...)0%660%
GetNameValueLength(...)100%11100%
GetNameValueLength(...)91.66%1212100%
GetNameValueListLength(...)90%1010100%
Find(...)0%10100%
GetValueLength(...)100%66100%
CheckNameValueFormat(...)100%110%
CheckValueFormat(...)0%18180%
ThrowFormatException(System.String)100%110%
CreateNameValue()100%11100%
System.ICloneable.Clone()100%110%

File(s)

D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\Headers\NameValueHeaderValue.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;
 6using System.Text;
 7
 8namespace System.Net.Http.Headers
 9{
 10    // According to the RFC, in places where a "parameter" is required, the value is mandatory
 11    // (e.g. Media-Type, Accept). However, we don't introduce a dedicated type for it. So NameValueHeaderValue supports
 12    // name-only values in addition to name/value pairs.
 13    public class NameValueHeaderValue : ICloneable
 14    {
 115        private static readonly Func<NameValueHeaderValue> s_defaultNameValueCreator = CreateNameValue;
 16
 64053717        private string _name = null!; // Name always set after default constructor used
 18        private string? _value;
 19
 20        public string Name
 21        {
 69748222            get { return _name; }
 23        }
 24
 25        public string? Value
 26        {
 69748227            get { return _value; }
 28            set
 029            {
 030                CheckValueFormat(value);
 031                _value = value;
 032            }
 33        }
 34
 64053735        internal NameValueHeaderValue()
 64053736        {
 64053737        }
 38
 39        public NameValueHeaderValue(string name)
 040            : this(name, null)
 041        {
 042        }
 43
 044        public NameValueHeaderValue(string name, string? value)
 045        {
 046            CheckNameValueFormat(name, value);
 47
 048            _name = name;
 049            _value = value;
 050        }
 51
 052        protected internal NameValueHeaderValue(NameValueHeaderValue source)
 053        {
 054            Debug.Assert(source != null);
 55
 056            _name = source._name;
 057            _value = source._value;
 058        }
 59
 60        public override int GetHashCode()
 061        {
 062            Debug.Assert(_name != null);
 63
 064            int nameHashCode = StringComparer.OrdinalIgnoreCase.GetHashCode(_name);
 65
 066            if (!string.IsNullOrEmpty(_value))
 067            {
 68                // If we have a quoted-string, then just use the hash code. If we have a token, convert to lowercase
 69                // and retrieve the hash code.
 070                if (_value[0] == '"')
 071                {
 072                    return nameHashCode ^ _value.GetHashCode();
 73                }
 74
 075                return nameHashCode ^ StringComparer.OrdinalIgnoreCase.GetHashCode(_value);
 76            }
 77
 078            return nameHashCode;
 079        }
 80
 81        public override bool Equals([NotNullWhen(true)] object? obj)
 082        {
 083            NameValueHeaderValue? other = obj as NameValueHeaderValue;
 84
 085            if (other == null)
 086            {
 087                return false;
 88            }
 89
 090            if (!string.Equals(_name, other._name, StringComparison.OrdinalIgnoreCase))
 091            {
 092                return false;
 93            }
 94
 95            // RFC2616: 14.20: unquoted tokens should use case-INsensitive comparison; quoted-strings should use
 96            // case-sensitive comparison. The RFC doesn't mention how to compare quoted-strings outside the "Expect"
 97            // header. We treat all quoted-strings the same: case-sensitive comparison.
 98
 099            if (string.IsNullOrEmpty(_value))
 0100            {
 0101                return string.IsNullOrEmpty(other._value);
 102            }
 103
 0104            if (_value[0] == '"')
 0105            {
 106                // We have a quoted string, so we need to do case-sensitive comparison.
 0107                return string.Equals(_value, other._value, StringComparison.Ordinal);
 108            }
 109            else
 0110            {
 0111                return string.Equals(_value, other._value, StringComparison.OrdinalIgnoreCase);
 112            }
 0113        }
 114
 115        public static NameValueHeaderValue Parse(string input)
 0116        {
 0117            int index = 0;
 0118            return (NameValueHeaderValue)GenericHeaderParser.SingleValueNameValueParser.ParseValue(
 0119                input, null, ref index);
 0120        }
 121
 122        public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true)] out NameValueHeaderValue? par
 0123        {
 0124            int index = 0;
 0125            parsedValue = null;
 126
 0127            if (GenericHeaderParser.SingleValueNameValueParser.TryParseValue(input, null, ref index, out object? output)
 0128            {
 0129                parsedValue = (NameValueHeaderValue)output!;
 0130                return true;
 131            }
 0132            return false;
 0133        }
 134
 135        public override string ToString()
 279645136        {
 279645137            if (!string.IsNullOrEmpty(_value))
 2400138            {
 2400139                return _name + "=" + _value;
 140            }
 277245141            return _name;
 279645142        }
 143
 144        private void AddToStringBuilder(StringBuilder sb)
 766577145        {
 766577146            if (GetType() != typeof(NameValueHeaderValue))
 0147            {
 148                // If this is a derived instance, we need to give its
 149                // ToString a chance.
 0150                sb.Append(ToString());
 0151            }
 152            else
 766577153            {
 154                // Otherwise, we can use the base behavior and avoid
 155                // the string concatenation.
 766577156                sb.Append(_name);
 766577157                if (!string.IsNullOrEmpty(_value))
 930158                {
 930159                    sb.Append('=');
 930160                    sb.Append(_value);
 930161                }
 766577162            }
 766577163        }
 164
 165        internal static void ToString(UnvalidatedObjectCollection<NameValueHeaderValue>? values, char separator, bool le
 166            StringBuilder destination)
 826406167        {
 826406168            Debug.Assert(destination != null);
 169
 826406170            if ((values == null) || (values.Count == 0))
 627913171            {
 627913172                return;
 173            }
 174
 2128633175            foreach (var value in values)
 766577176            {
 766577177                if (leadingSeparator || (destination.Length > 0))
 761579178                {
 761579179                    destination.Append(separator);
 761579180                    destination.Append(' ');
 761579181                }
 766577182                value.AddToStringBuilder(destination);
 766577183            }
 826406184        }
 185
 186        internal static int GetHashCode(UnvalidatedObjectCollection<NameValueHeaderValue>? values)
 0187        {
 0188            if ((values == null) || (values.Count == 0))
 0189            {
 0190                return 0;
 191            }
 192
 0193            int result = 0;
 0194            foreach (var value in values)
 0195            {
 0196                result ^= value.GetHashCode();
 0197            }
 0198            return result;
 0199        }
 200
 201        internal static int GetNameValueLength(string input, int startIndex, out NameValueHeaderValue? parsedValue)
 237688202        {
 237688203            return GetNameValueLength(input, startIndex, s_defaultNameValueCreator, out parsedValue);
 237688204        }
 205
 206        internal static int GetNameValueLength(string input, int startIndex,
 207            Func<NameValueHeaderValue> nameValueCreator, out NameValueHeaderValue? parsedValue)
 641343208        {
 641343209            Debug.Assert(input != null);
 641343210            Debug.Assert(startIndex >= 0);
 641343211            Debug.Assert(nameValueCreator != null);
 212
 641343213            parsedValue = null;
 214
 641343215            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
 38216            {
 38217                return 0;
 218            }
 219
 220            // Parse the name, i.e. <name> in name/value string "<name>=<value>". Caller must remove
 221            // leading whitespace.
 641305222            int nameLength = HttpRuleParser.GetTokenLength(input, startIndex);
 223
 641305224            if (nameLength == 0)
 430225            {
 430226                return 0;
 227            }
 228
 640875229            string name = input.Substring(startIndex, nameLength);
 640875230            int current = startIndex + nameLength;
 640875231            current += HttpRuleParser.GetWhitespaceLength(input, current);
 232
 233            // Parse the separator between name and value
 640875234            if ((current == input.Length) || (input[current] != '='))
 638246235            {
 236                // We only have a name and that's OK. Return.
 638246237                parsedValue = nameValueCreator();
 638246238                parsedValue._name = name;
 638246239                current += HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespace
 638246240                return current - startIndex;
 241            }
 242
 2629243            current++; // skip delimiter.
 2629244            current += HttpRuleParser.GetWhitespaceLength(input, current);
 245
 246            // Parse the value, i.e. <value> in name/value string "<name>=<value>"
 2629247            int valueLength = GetValueLength(input, current);
 248
 2629249            if (valueLength == 0)
 338250            {
 338251                return 0; // We have an invalid value.
 252            }
 253
 254            // Use parameterless ctor to avoid double-parsing of name and value, i.e. skip public ctor validation.
 2291255            parsedValue = nameValueCreator();
 2291256            parsedValue._name = name;
 2291257            parsedValue._value = input.Substring(current, valueLength);
 2291258            current += valueLength;
 2291259            current += HttpRuleParser.GetWhitespaceLength(input, current); // skip whitespace
 2291260            return current - startIndex;
 641343261        }
 262
 263        // Returns the length of a name/value list, separated by 'delimiter'. E.g. "a=b, c=d, e=f" adds 3
 264        // name/value pairs to 'nameValueCollection' if 'delimiter' equals ','.
 265        internal static int GetNameValueListLength(string? input, int startIndex, char delimiter,
 266            UnvalidatedObjectCollection<NameValueHeaderValue> nameValueCollection)
 122459267        {
 122459268            Debug.Assert(nameValueCollection != null);
 122459269            Debug.Assert(startIndex >= 0);
 270
 122459271            if ((string.IsNullOrEmpty(input)) || (startIndex >= input.Length))
 54272            {
 54273                return 0;
 274            }
 275
 122405276            int current = startIndex + HttpRuleParser.GetWhitespaceLength(input, startIndex);
 239958277            while (true)
 239958278            {
 279                NameValueHeaderValue? parameter;
 239958280                int nameValueLength = NameValueHeaderValue.GetNameValueLength(input, current,
 239958281                    s_defaultNameValueCreator, out parameter);
 282
 239958283                if (nameValueLength == 0)
 382284                {
 382285                    return 0;
 286                }
 287
 239576288                nameValueCollection.Add(parameter!);
 239576289                current += nameValueLength;
 239576290                current += HttpRuleParser.GetWhitespaceLength(input, current);
 291
 239576292                if ((current == input.Length) || (input[current] != delimiter))
 122023293                {
 294                    // We're done and we have at least one valid name/value pair.
 122023295                    return current - startIndex;
 296                }
 297
 298                // input[current] is 'delimiter'. Skip the delimiter and whitespace and try to parse again.
 117553299                current++; // skip delimiter.
 117553300                current += HttpRuleParser.GetWhitespaceLength(input, current);
 117553301            }
 122459302        }
 303
 304        internal static NameValueHeaderValue? Find(UnvalidatedObjectCollection<NameValueHeaderValue>? values, string nam
 0305        {
 0306            Debug.Assert((name != null) && (name.Length > 0));
 307
 0308            if ((values == null) || (values.Count == 0))
 0309            {
 0310                return null;
 311            }
 312
 0313            foreach (var value in values)
 0314            {
 0315                if (string.Equals(value.Name, name, StringComparison.OrdinalIgnoreCase))
 0316                {
 0317                    return value;
 318                }
 0319            }
 0320            return null;
 0321        }
 322
 323        internal static int GetValueLength(string input, int startIndex)
 3579324        {
 3579325            Debug.Assert(input != null);
 326
 3579327            if (startIndex >= input.Length)
 28328            {
 28329                return 0;
 330            }
 331
 3551332            int valueLength = HttpRuleParser.GetTokenLength(input, startIndex);
 333
 3551334            if (valueLength == 0)
 1003335            {
 336                // A value can either be a token or a quoted string. Check if it is a quoted string.
 1003337                if (HttpRuleParser.GetQuotedStringLength(input, startIndex, out valueLength) != HttpParseResult.Parsed)
 340338                {
 339                    // We have an invalid value. Reset the name and return.
 340340                    return 0;
 341                }
 663342            }
 3211343            return valueLength;
 3579344        }
 345
 346        private static void CheckNameValueFormat(string name, string? value)
 0347        {
 0348            HeaderUtilities.CheckValidToken(name);
 0349            CheckValueFormat(value);
 0350        }
 351
 352        private static void CheckValueFormat(string? value)
 0353        {
 354            // Either value is null/empty or a valid token/quoted string https://tools.ietf.org/html/rfc7230#section-3.2
 0355            if (string.IsNullOrEmpty(value))
 0356            {
 0357                return;
 358            }
 359
 360            // Trailing/leading space are not allowed
 0361            if (value.StartsWith(' ') || value.StartsWith('\t') || value.EndsWith(' ') || value.EndsWith('\t'))
 0362            {
 0363                ThrowFormatException(value);
 0364            }
 365
 0366            if (value[0] == '"')
 0367            {
 0368                HttpParseResult parseResult = HttpRuleParser.GetQuotedStringLength(value, 0, out int valueLength);
 0369                if (parseResult != HttpParseResult.Parsed || valueLength != value.Length)
 0370                {
 0371                    ThrowFormatException(value);
 0372                }
 0373            }
 0374            else if (HttpRuleParser.ContainsNewLineOrNull(value))
 0375            {
 0376                ThrowFormatException(value);
 0377            }
 378
 379            static void ThrowFormatException(string value) =>
 0380                throw new FormatException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_heade
 0381        }
 382
 383        private static NameValueHeaderValue CreateNameValue()
 476944384        {
 476944385            return new NameValueHeaderValue();
 476944386        }
 387
 388        // Implement ICloneable explicitly to allow derived types to "override" the implementation.
 389        object ICloneable.Clone()
 0390        {
 0391            return new NameValueHeaderValue(this);
 0392        }
 393    }
 394}