< Summary

Information
Class: System.Net.Http.Headers.HttpHeaders
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\Headers\HttpHeaders.cs
Line coverage
41%
Covered lines: 444
Uncovered lines: 615
Coverable lines: 1059
Total lines: 1664
Line coverage: 41.9%
Branch coverage
39%
Covered branches: 160
Total branches: 402
Branch coverage: 39.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor()100%110%
.ctor(...)100%11100%
Add(...)100%11100%
Add(...)100%66100%
Add(...)100%110%
Add(...)0%16160%
TryAddWithoutValidation(...)100%22100%
TryAddWithoutValidation(...)83.33%66100%
TryAddWithoutValidation(...)0%220%
TryAddWithoutValidation(...)0%24240%
GetValues(...)100%110%
GetValues(...)0%220%
TryGetValues(...)0%220%
TryGetValues(...)0%660%
Contains(...)100%110%
ToString()100%110%
Dump(...)0%10100%
GetHeaderString(...)0%440%
GetEnumerator()100%22100%
GetEnumeratorCore()80%1010100%
System.Collections.IEnumerable.GetEnumerator()100%110%
AddParsedValue(...)0%220%
SetParsedValue(...)100%110%
SetOrRemoveParsedValue(...)0%220%
Remove(...)100%110%
RemoveParsedValue(...)0%28280%
ContainsParsedValue(...)0%14140%
AddHeaders(...)0%20200%
CloneHeaderInfo(...)0%880%
CloneAndAddValue(...)0%220%
CloneStringHeaderInfoValues(...)0%440%
GetOrCreateHeaderInfo(...)0%220%
CreateAndAddHeaderToStore(...)100%110%
TryGetHeaderValue(...)0%220%
TryGetAndParseHeaderInfo(...)75%44100%
ReplaceWithHeaderStoreItemInfo(...)50%2288.88%
ParseRawHeaderValues(...)100%66100%
ParseSingleRawHeaderValue(...)60%101080%
TryParseAndAddValue(...)0%440%
TryParseAndAddRawHeaderValue(...)72.5%404085.48%
AddParsedValue(...)100%11100%
AddInvalidValue(...)100%11100%
AddRawValue(...)100%11100%
AddValueToStoreValue(...)100%44100%
GetSingleParsedValue(...)0%220%
GetParsedAndInvalidValues(...)0%220%
IsAllowedHeaderName(...)100%11100%
CheckIsAllowedHeaderName(...)50%2260%
PrepareHeaderInfoForAdd(...)100%22100%
ParseAndAddValue(...)93.75%1616100%
GetHeaderDescriptor(...)66.66%6676.92%
TryGetHeaderDescriptor(...)87.5%8883.33%
CheckContainsNewLineOrNull(...)100%44100%
GetStoreValuesAsStringArray(...)100%22100%
GetStoreValuesAsStringOrStringArray(...)75%4481.48%
GetStoreValuesIntoStringArray(...)0%880%
GetValueCount(...)100%11100%
Count(System.Object)100%44100%
ReadStoreValues(...)100%1414100%
AreEqual(...)0%220%
.ctor(...)100%11100%
ToString()100%11100%
.ctor()100%11100%
CanAddParsedValue(...)100%22100%
AssertContainsNoInvalidValues()100%66100%
GetSingleParsedValue()0%10100%
AssertContainsSingleParsedValue(...)0%440%
GetEntriesArray()50%4463.63%
GetEntriesFromDictionary()0%220%
GetEntries()100%110%
GetValueRefOrNullRef(...)70%101073.68%
GetValueRefOrAddDefault(...)75%121285.18%
GrowEntriesAndAddDefault(System.Net.Http.Headers.HeaderDescriptor)0%220%
ConvertToDictionaryAndAddDefault(System.Net.Http.Headers.HeaderDescriptor)0%220%
DictionaryGetValueRefOrAddDefault(System.Net.Http.Headers.HeaderDescriptor)0%220%
AddEntryToStore(...)50%4484.61%
Contains(...)100%11100%
Clear()100%22100%
Remove(...)0%16160%

File(s)

D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\Headers\HttpHeaders.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.Collections;
 5using System.Collections.Generic;
 6using System.Diagnostics;
 7using System.Diagnostics.CodeAnalysis;
 8using System.Runtime.CompilerServices;
 9using System.Runtime.InteropServices;
 10using System.Text;
 11using System.Threading;
 12
 13namespace System.Net.Http.Headers
 14{
 15    /// <summary>
 16    /// Key/value pairs of headers. The value is either a raw <see cref="string"/> or a <see cref="HttpHeaders.HeaderSto
 17    /// We're using a custom type instead of <see cref="KeyValuePair{TKey, TValue}"/> because we need ref access to fiel
 18    /// </summary>
 19    internal struct HeaderEntry
 20    {
 21        public HeaderDescriptor Key;
 22        public object Value;
 23
 24        public HeaderEntry(HeaderDescriptor key, object value)
 25        {
 26            Key = key;
 27            Value = value;
 28        }
 29    }
 30
 31    public abstract class HttpHeaders : IEnumerable<KeyValuePair<string, IEnumerable<string>>>
 32    {
 33        // This type is used to store a collection of headers in 'headerStore':
 34        // - A header can have multiple values.
 35        // - A header can have an associated parser which is able to parse the raw string value into a strongly typed ob
 36        // - If a header has an associated parser and the provided raw value can't be parsed, the value is considered
 37        //   invalid. Invalid values are stored if added using TryAddWithoutValidation(). If the value was added using A
 38        //   Add() will throw FormatException.
 39        // - Since parsing header values is expensive and users usually only care about a few headers, header values are
 40        //   lazily initialized.
 41        //
 42        // Given the properties above, a header value can have three states:
 43        // - 'raw': The header value was added using TryAddWithoutValidation() and it wasn't parsed yet.
 44        // - 'parsed': The header value was successfully parsed. It was either added using Add() where the value was par
 45        //   immediately, or if added using TryAddWithoutValidation() a user already accessed a property/method triggeri
 46        //   value to be parsed.
 47        // - 'invalid': The header value was parsed, but parsing failed because the value is invalid. Storing invalid va
 48        //   allows users to still retrieve the value (by calling GetValues()), but it will not be exposed as strongly t
 49        //   object. E.g. the client receives a response with the following header: 'Via: 1.1 proxy, invalid'
 50        //   - HttpHeaders.GetValues() will return "1.1 proxy", "invalid"
 51        //   - HttpResponseHeaders.Via collection will only contain one ViaHeaderValue object with value "1.1 proxy"
 52
 53        /// <summary>Either a <see cref="HeaderEntry"/> array or a Dictionary&lt;<see cref="HeaderDescriptor"/>, <see cr
 54        private object? _headerStore;
 55        private int _count;
 56
 57        private readonly HttpHeaderType _allowedHeaderTypes;
 58        private readonly HttpHeaderType _treatAsCustomHeaderTypes;
 59
 60        protected HttpHeaders()
 061            : this(HttpHeaderType.All, HttpHeaderType.None)
 062        {
 063        }
 64
 365        internal HttpHeaders(HttpHeaderType allowedHeaderTypes, HttpHeaderType treatAsCustomHeaderTypes)
 366        {
 67            // Should be no overlap
 368            Debug.Assert((allowedHeaderTypes & treatAsCustomHeaderTypes) == 0);
 69
 370            _allowedHeaderTypes = allowedHeaderTypes & ~HttpHeaderType.NonTrailing;
 371            _treatAsCustomHeaderTypes = treatAsCustomHeaderTypes & ~HttpHeaderType.NonTrailing;
 372        }
 73
 74        /// <summary>Gets a view of the contents of this headers collection that does not parse nor validate the data up
 2510775        public HttpHeadersNonValidated NonValidated => new HttpHeadersNonValidated(this);
 76
 3896977        public void Add(string name, string? value) => Add(GetHeaderDescriptor(name), value);
 78
 79        internal void Add(HeaderDescriptor descriptor, string? value)
 3896980        {
 3896981            if (descriptor.Parser is null)
 1411182            {
 83                // If the header has no parser, we only have to check for new lines or null.
 1411184                CheckIsAllowedHeaderName(descriptor);
 1411185                CheckContainsNewLineOrNull(value);
 1040686                TryAddWithoutValidation(descriptor, value);
 1040687                return;
 88            }
 89
 90            // We don't use GetOrCreateHeaderInfo() here, since this would create a new header in the store. If parsing
 91            // the value then throws, we would have to remove the header from the store again. So just get a
 92            // HeaderStoreItemInfo object and try to parse the value. If it works, we'll add the header.
 2485893            PrepareHeaderInfoForAdd(descriptor, out HeaderStoreItemInfo info, out bool addToStore);
 2485894            ParseAndAddValue(descriptor, info, value);
 95
 96            // If we get here, then the value could be parsed correctly. If we created a new HeaderStoreItemInfo, add
 97            // it to the store if we added at least one value.
 1689998            if (addToStore && (info.ParsedAndInvalidValues != null))
 854199            {
 8541100                info.AssertContainsNoInvalidValues();
 8541101                Debug.Assert(!Contains(descriptor));
 8541102                AddEntryToStore(new HeaderEntry(descriptor, info));
 8541103            }
 27305104        }
 105
 0106        public void Add(string name, IEnumerable<string?> values) => Add(GetHeaderDescriptor(name), values);
 107
 108        internal void Add(HeaderDescriptor descriptor, IEnumerable<string?> values)
 0109        {
 0110            ArgumentNullException.ThrowIfNull(values);
 111
 112            // It's relatively common to only add a single value with this overload, especially when copying
 113            // between HttpHeaders collections. Avoid boxing the enumerator and possibly a HeaderStoreItemInfo
 114            // allocation by deferring to the overload for a single value instead.
 0115            if (values is IList<string?> { Count: 1 } valuesList)
 0116            {
 0117                Add(descriptor, valuesList[0]);
 0118                return;
 119            }
 120
 0121            PrepareHeaderInfoForAdd(descriptor, out HeaderStoreItemInfo info, out bool addToStore);
 122
 123            try
 0124            {
 125                // Note that if the first couple of values are valid followed by an invalid value, the valid values
 126                // will be added to the store before the exception for the invalid value is thrown.
 0127                if (descriptor.Parser is null)
 0128                {
 0129                    foreach (string? value in values)
 0130                    {
 131                        // If the header has no parser, we only have to check for new lines or null.
 0132                        CheckContainsNewLineOrNull(value);
 0133                        AddParsedValue(info, value ?? string.Empty);
 0134                    }
 0135                }
 136                else
 0137                {
 0138                    foreach (string? value in values)
 0139                    {
 0140                        ParseAndAddValue(descriptor, info, value);
 0141                    }
 0142                }
 0143            }
 144            finally
 0145            {
 146                // Even if one of the values was invalid, make sure we add the header for the valid ones. We need to be
 147                // consistent here: If values get added to an _existing_ header, then all values until the invalid one
 148                // get added. Same here: If multiple values get added to a _new_ header, make sure the header gets added
 149                // with the valid values.
 150                // However, if all values for a _new_ header were invalid, then don't add the header.
 0151                if (addToStore && (info.ParsedAndInvalidValues != null))
 0152                {
 0153                    info.AssertContainsNoInvalidValues();
 0154                    Debug.Assert(!Contains(descriptor));
 0155                    AddEntryToStore(new HeaderEntry(descriptor, info));
 0156                }
 0157            }
 0158        }
 159
 160        public bool TryAddWithoutValidation(string name, string? value) =>
 37662161            TryGetHeaderDescriptor(name, out HeaderDescriptor descriptor) &&
 37662162            TryAddWithoutValidation(descriptor, value);
 163
 164        internal bool TryAddWithoutValidation(HeaderDescriptor descriptor, string? value)
 35513165        {
 166            // Normalize null values to be empty values, which are allowed. If the user adds multiple
 167            // null/empty values, all of them are added to the collection. This will result in delimiter-only
 168            // values, e.g. adding two null-strings (or empty, or whitespace-only) results in "My-Header: ,".
 35513169            value ??= string.Empty;
 170
 35513171            ref object? storeValueRef = ref GetValueRefOrAddDefault(descriptor);
 35513172            object? currentValue = storeValueRef;
 173
 35513174            if (currentValue is null)
 30310175            {
 30310176                storeValueRef = value;
 30310177            }
 178            else
 5203179            {
 5203180                if (currentValue is not HeaderStoreItemInfo info)
 5203181                {
 182                    // The header store contained a single raw string value, so promote it
 183                    // to being a HeaderStoreItemInfo and add to it.
 5203184                    Debug.Assert(currentValue is string);
 5203185                    storeValueRef = info = new HeaderStoreItemInfo() { RawValue = currentValue };
 5203186                }
 187
 5203188                AddRawValue(info, value);
 5203189            }
 190
 35513191            return true;
 35513192        }
 193
 194        public bool TryAddWithoutValidation(string name, IEnumerable<string?> values) =>
 0195            TryGetHeaderDescriptor(name, out HeaderDescriptor descriptor) &&
 0196            TryAddWithoutValidation(descriptor, values);
 197
 198        internal bool TryAddWithoutValidation(HeaderDescriptor descriptor, IEnumerable<string?> values)
 0199        {
 0200            ArgumentNullException.ThrowIfNull(values);
 201
 0202            if (values is IList<string?> valuesList)
 0203            {
 0204                int count = valuesList.Count;
 205
 0206                if (count > 0)
 0207                {
 208                    // The store value is either a string (a single unparsed value) or a HeaderStoreItemInfo.
 209                    // The RawValue on HeaderStoreItemInfo can likewise be either a single string or a List<string>.
 210
 0211                    ref object? storeValueRef = ref GetValueRefOrAddDefault(descriptor);
 0212                    object? storeValue = storeValueRef;
 213
 214                    // If the storeValue was already set or we're adding more than 1 value,
 215                    // we'll have to store the values in a List<string> on HeaderStoreItemInfo.
 0216                    if (storeValue is not null || count > 1)
 0217                    {
 0218                        if (storeValue is not HeaderStoreItemInfo info)
 0219                        {
 0220                            storeValueRef = info = new HeaderStoreItemInfo { RawValue = storeValue };
 0221                        }
 222
 0223                        object? rawValue = info.RawValue;
 0224                        if (rawValue is not List<string> rawValues)
 0225                        {
 0226                            info.RawValue = rawValues = new List<string>();
 227
 0228                            if (rawValue != null)
 0229                            {
 0230                                rawValues.EnsureCapacity(count + 1);
 0231                                rawValues.Add((string)rawValue);
 0232                            }
 0233                        }
 234
 0235                        rawValues.EnsureCapacity(rawValues.Count + count);
 236
 0237                        for (int i = 0; i < count; i++)
 0238                        {
 0239                            rawValues.Add(valuesList[i] ?? string.Empty);
 0240                        }
 0241                    }
 242                    else
 0243                    {
 244                        // We're adding a single value to a new header entry. We can store the unparsed value as-is.
 0245                        storeValueRef = valuesList[0] ?? string.Empty;
 0246                    }
 0247                }
 0248            }
 249            else
 0250            {
 0251                foreach (string? value in values)
 0252                {
 0253                    TryAddWithoutValidation(descriptor, value ?? string.Empty);
 0254                }
 0255            }
 256
 0257            return true;
 0258        }
 259
 0260        public IEnumerable<string> GetValues(string name) => GetValues(GetHeaderDescriptor(name));
 261
 262        internal IEnumerable<string> GetValues(HeaderDescriptor descriptor)
 0263        {
 0264            if (TryGetValues(descriptor, out IEnumerable<string>? values))
 0265            {
 0266                return values;
 267            }
 268
 0269            throw new InvalidOperationException(SR.net_http_headers_not_found);
 0270        }
 271
 272        public bool TryGetValues(string name, [NotNullWhen(true)] out IEnumerable<string>? values)
 0273        {
 0274            if (TryGetHeaderDescriptor(name, out HeaderDescriptor descriptor))
 0275            {
 0276                return TryGetValues(descriptor, out values);
 277            }
 278
 0279            values = null;
 0280            return false;
 0281        }
 282
 283        internal bool TryGetValues(HeaderDescriptor descriptor, [NotNullWhen(true)] out IEnumerable<string>? values)
 0284        {
 0285            ref object storeValueRef = ref GetValueRefOrNullRef(descriptor);
 0286            if (!Unsafe.IsNullRef(ref storeValueRef))
 0287            {
 0288                object value = storeValueRef;
 289
 0290                if (value is not HeaderStoreItemInfo info)
 0291                {
 0292                    if (descriptor.Parser is null)
 0293                    {
 294                        // This is a custom header without a known parser, so unparsed values won't change.
 295                        // Avoid allocating the HeaderStoreItemInfo and just return the raw value as-is.
 0296                        values = new string[] { (string)value };
 0297                        return true;
 298                    }
 299
 0300                    info = ReplaceWithHeaderStoreItemInfo(ref storeValueRef, value);
 0301                }
 302
 0303                ParseRawHeaderValues(descriptor, info);
 0304                values = GetStoreValuesAsStringArray(descriptor, info);
 0305                return true;
 306            }
 307
 0308            values = null;
 0309            return false;
 0310        }
 311
 0312        public bool Contains(string name) => Contains(GetHeaderDescriptor(name));
 313
 314        public override string ToString()
 0315        {
 0316            var vsb = new ValueStringBuilder(stackalloc char[512]);
 0317            Dump(ref vsb, indentLines: false);
 0318            return vsb.ToString();
 0319        }
 320
 321        internal void Dump(ref ValueStringBuilder builder, bool indentLines)
 0322        {
 323            // Return all headers as string similar to:
 324            // HeaderName1: Value1, Value2
 325            // HeaderName2: Value1
 326            // ...
 327
 0328            foreach (HeaderEntry entry in GetEntries())
 0329            {
 0330                if (indentLines)
 0331                {
 0332                    builder.Append("  ");
 0333                }
 334
 0335                builder.Append(entry.Key.Name);
 0336                builder.Append(": ");
 337
 0338                GetStoreValuesAsStringOrStringArray(entry.Key, entry.Value, out string? singleValue, out string[]? multi
 0339                Debug.Assert(singleValue is not null ^ multiValue is not null);
 340
 0341                if (singleValue is not null)
 0342                {
 0343                    builder.Append(singleValue);
 0344                }
 345                else
 0346                {
 347                    // Note that if we get multiple values for a header that doesn't support multiple values, we'll
 348                    // just separate the values using a comma (default separator).
 0349                    string separator = entry.Key.Separator;
 350
 0351                    Debug.Assert(multiValue is not null && multiValue.Length > 0);
 0352                    builder.Append(multiValue[0]);
 0353                    for (int i = 1; i < multiValue.Length; i++)
 0354                    {
 0355                        builder.Append(separator);
 0356                        builder.Append(multiValue[i]);
 0357                    }
 0358                }
 359
 0360                builder.Append(Environment.NewLine);
 0361            }
 0362        }
 363
 364        internal string GetHeaderString(HeaderDescriptor descriptor)
 0365        {
 0366            if (TryGetHeaderValue(descriptor, out object? info))
 0367            {
 0368                GetStoreValuesAsStringOrStringArray(descriptor, info, out string? singleValue, out string[]? multiValue)
 0369                Debug.Assert(singleValue is not null ^ multiValue is not null);
 370
 0371                if (singleValue is not null)
 0372                {
 0373                    return singleValue;
 374                }
 375
 376                // Note that if we get multiple values for a header that doesn't support multiple values, we'll
 377                // just separate the values using a comma (default separator).
 0378                return string.Join(descriptor.Separator, multiValue!);
 379            }
 380
 0381            return string.Empty;
 0382        }
 383
 384        #region IEnumerable<KeyValuePair<string, IEnumerable<string>>> Members
 385
 50214386        public IEnumerator<KeyValuePair<string, IEnumerable<string>>> GetEnumerator() => _count == 0 ?
 50214387                ((IEnumerable<KeyValuePair<string, IEnumerable<string>>>)Array.Empty<KeyValuePair<string, IEnumerable<st
 50214388                GetEnumeratorCore();
 389
 390        private IEnumerator<KeyValuePair<string, IEnumerable<string>>> GetEnumeratorCore()
 38851391        {
 38851392            Debug.Assert(_headerStore is not null);
 393
 38851394            HeaderEntry[]? entries = GetEntriesArray();
 38851395            Debug.Assert(_count != 0 && entries is not null, "Caller should have validated the collection is not empty")
 396
 155404397            for (int i = 0; i < _count; i++)
 38851398            {
 38851399                HeaderEntry entry = entries[i];
 400
 38851401                if (entry.Value is not HeaderStoreItemInfo info)
 25107402                {
 25107403                    if (entry.Key.Parser is null)
 8908404                    {
 405                        // This is a custom header without a known parser, so unparsed values won't change.
 406                        // Avoid allocating the HeaderStoreItemInfo and just return the raw value as-is.
 8908407                        yield return new KeyValuePair<string, IEnumerable<string>>(entry.Key.Name, new string[] { (strin
 8908408                        continue;
 409                    }
 410
 411                    // To retain consistent semantics, we need to upgrade a raw string to a HeaderStoreItemInfo
 412                    // during enumeration so that we can parse the raw value in order to a) return
 413                    // the correct set of parsed values, and b) update the instance for subsequent enumerations
 414                    // to reflect that parsing.
 16199415                    ref object storeValueRef = ref EntriesAreLiveView
 16199416                        ? ref entries[i].Value
 16199417                        : ref CollectionsMarshal.GetValueRefOrNullRef((Dictionary<HeaderDescriptor, object>)_headerStore
 418
 16199419                    info = ReplaceWithHeaderStoreItemInfo(ref storeValueRef, entry.Value);
 16199420                }
 421
 422                // Make sure we parse all raw values before returning the result. Note that this has to be
 423                // done before we calculate the array length (next line): A raw value may contain a list of
 424                // values.
 29943425                ParseRawHeaderValues(entry.Key, info);
 426
 29943427                string[] values = GetStoreValuesAsStringArray(entry.Key, info);
 29943428                yield return new KeyValuePair<string, IEnumerable<string>>(entry.Key.Name, values);
 29943429            }
 38851430        }
 431
 432        #endregion
 433
 434        #region IEnumerable Members
 435
 0436        Collections.IEnumerator Collections.IEnumerable.GetEnumerator() => GetEnumerator();
 437
 438        #endregion
 439
 440        internal void AddParsedValue(HeaderDescriptor descriptor, object value)
 0441        {
 0442            Debug.Assert(value != null);
 0443            Debug.Assert(descriptor.Parser != null, "Can't add parsed value if there is no parser available.");
 444
 0445            HeaderStoreItemInfo info = GetOrCreateHeaderInfo(descriptor);
 446
 447            // If the current header has only one value, we can't add another value. The strongly typed property
 448            // must not call AddParsedValue(), but SetParsedValue(). E.g. for headers like 'Date', 'Host'.
 0449            Debug.Assert(descriptor.Parser.SupportsMultipleValues, $"Header '{descriptor.Name}' doesn't support multiple
 450
 0451            AddParsedValue(info, value);
 0452        }
 453
 454        internal void SetParsedValue(HeaderDescriptor descriptor, object value)
 0455        {
 0456            Debug.Assert(value != null);
 0457            Debug.Assert(descriptor.Parser != null, "Can't add parsed value if there is no parser available.");
 458
 459            // This method will first clear all values. This is used e.g. when setting the 'Date' or 'Host' header.
 460            // i.e. headers not supporting collections.
 0461            HeaderStoreItemInfo info = GetOrCreateHeaderInfo(descriptor);
 462
 0463            info.ParsedAndInvalidValues = null;
 0464            info.RawValue = null;
 465
 0466            AddParsedValue(info, value);
 0467        }
 468
 469        internal void SetOrRemoveParsedValue(HeaderDescriptor descriptor, object? value)
 0470        {
 0471            if (value == null)
 0472            {
 0473                Remove(descriptor);
 0474            }
 475            else
 0476            {
 0477                SetParsedValue(descriptor, value);
 0478            }
 0479        }
 480
 0481        public bool Remove(string name) => Remove(GetHeaderDescriptor(name));
 482
 483        internal bool RemoveParsedValue(HeaderDescriptor descriptor, object value, bool removeAll = false)
 0484        {
 0485            Debug.Assert(value != null);
 486
 487            // If we have a value for this header, then verify if we have a single value. If so, compare that
 488            // value with 'item'. If we have a list of values, then remove 'item' from the list.
 0489            if (TryGetAndParseHeaderInfo(descriptor, out HeaderStoreItemInfo? info))
 0490            {
 0491                Debug.Assert(descriptor.Parser != null, "Can't add parsed value if there is no parser available.");
 0492                Debug.Assert(descriptor.Parser.SupportsMultipleValues,
 0493                    "This method should not be used for single-value headers. Use Remove(string) instead.");
 494
 495                // If there is no entry, just return.
 0496                var parsedValue = info.ParsedAndInvalidValues;
 0497                if (parsedValue == null)
 0498                {
 0499                    return false;
 500                }
 501
 0502                bool result = false;
 0503                IEqualityComparer? comparer = descriptor.Parser.Comparer;
 504
 0505                List<object>? parsedValues = parsedValue as List<object>;
 0506                if (parsedValues == null)
 0507                {
 0508                    if (parsedValue is not InvalidValue)
 0509                    {
 0510                        Debug.Assert(parsedValue.GetType() == value.GetType(),
 0511                            "Stored value does not have the same type as 'value'.");
 512
 0513                        if (AreEqual(value, parsedValue, comparer))
 0514                        {
 0515                            info.ParsedAndInvalidValues = null;
 0516                            result = true;
 0517                        }
 0518                    }
 0519                }
 520                else
 0521                {
 0522                    for (int i = 0; i < parsedValues.Count; i++)
 0523                    {
 0524                        object item = parsedValues[i];
 0525                        if (item is not InvalidValue)
 0526                        {
 0527                            Debug.Assert(item.GetType() == value.GetType(),
 0528                                "One of the stored values does not have the same type as 'value'.");
 529
 0530                            if (AreEqual(value, item, comparer))
 0531                            {
 0532                                parsedValues.RemoveAt(i);
 0533                                i--;
 534
 0535                                if (!result)
 0536                                {
 0537                                    result = true;
 538
 0539                                    if (!removeAll)
 0540                                    {
 0541                                        break;
 542                                    }
 0543                                }
 544                                else
 0545                                {
 546                                    // We've removed a second item. Fallback to RemoveAll in case there are more to main
 547                                    // Create a copy of the locals to avoid the capture allocation in the common case.
 0548                                    object valueLocal = value;
 0549                                    IEqualityComparer? comparerLocal = comparer;
 0550                                    parsedValues.RemoveAll(item => item is not InvalidValue && AreEqual(valueLocal, item
 0551                                    break;
 552                                }
 0553                            }
 0554                        }
 0555                    }
 556
 557                    // If we removed the last item in a list, remove the list.
 0558                    if (parsedValues.Count == 0)
 0559                    {
 0560                        info.AssertContainsNoInvalidValues();
 0561                        info.ParsedAndInvalidValues = null;
 0562                    }
 0563                }
 564
 565                // If there is no value for the header left, remove the header.
 0566                if (info.IsEmpty)
 0567                {
 0568                    bool headerRemoved = Remove(descriptor);
 0569                    Debug.Assert(headerRemoved, $"Existing header '{descriptor.Name}' couldn't be removed.");
 0570                }
 571
 0572                return result;
 573            }
 574
 0575            return false;
 0576        }
 577
 578        internal bool ContainsParsedValue(HeaderDescriptor descriptor, object value)
 0579        {
 0580            Debug.Assert(value != null);
 581
 582            // If we have a value for this header, then verify if we have a single value. If so, compare that
 583            // value with 'item'. If we have a list of values, then compare each item in the list with 'item'.
 0584            if (TryGetAndParseHeaderInfo(descriptor, out HeaderStoreItemInfo? info))
 0585            {
 0586                Debug.Assert(descriptor.Parser != null, "Can't add parsed value if there is no parser available.");
 0587                Debug.Assert(descriptor.Parser.SupportsMultipleValues,
 0588                    "This method should not be used for single-value headers. Use equality comparer instead.");
 589
 590                // If there is no entry, just return.
 0591                var parsedValue = info.ParsedAndInvalidValues;
 0592                if (parsedValue == null)
 0593                {
 0594                    return false;
 595                }
 596
 0597                List<object>? parsedValues = parsedValue as List<object>;
 598
 0599                IEqualityComparer? comparer = descriptor.Parser.Comparer;
 600
 0601                if (parsedValues == null)
 0602                {
 0603                    if (parsedValue is not InvalidValue)
 0604                    {
 0605                        Debug.Assert(parsedValue.GetType() == value.GetType(),
 0606                            "Stored value does not have the same type as 'value'.");
 607
 0608                        return AreEqual(value, parsedValue, comparer);
 609                    }
 0610                }
 611                else
 0612                {
 0613                    foreach (object item in parsedValues)
 0614                    {
 0615                        if (item is not InvalidValue)
 0616                        {
 0617                            Debug.Assert(item.GetType() == value.GetType(),
 0618                                "One of the stored values does not have the same type as 'value'.");
 619
 0620                            if (AreEqual(value, item, comparer))
 0621                            {
 0622                                return true;
 623                            }
 0624                        }
 0625                    }
 626
 0627                    return false;
 628                }
 0629            }
 630
 0631            return false;
 0632        }
 633
 634        internal virtual void AddHeaders(HttpHeaders sourceHeaders)
 0635        {
 0636            Debug.Assert(sourceHeaders != null);
 0637            Debug.Assert(GetType() == sourceHeaders.GetType(), "Can only copy headers from an instance of the same type.
 638
 639            // Only add header values if they're not already set on the message. Note that we don't merge
 640            // collections: If both the default headers and the message have set some values for a certain
 641            // header, then we don't try to merge the values.
 0642            if (_count == 0 && sourceHeaders._headerStore is HeaderEntry[] sourceEntries)
 0643            {
 644                // If the target collection is empty, we don't have to search for existing values
 0645                _count = sourceHeaders._count;
 0646                if (_headerStore is not HeaderEntry[] entries || entries.Length < _count)
 0647                {
 0648                    entries = new HeaderEntry[sourceEntries.Length];
 0649                    _headerStore = entries;
 0650                }
 651
 0652                for (int i = 0; i < _count && i < sourceEntries.Length; i++)
 0653                {
 0654                    HeaderEntry entry = sourceEntries[i];
 0655                    if (entry.Value is HeaderStoreItemInfo info)
 0656                    {
 0657                        entry.Value = CloneHeaderInfo(entry.Key, info);
 0658                    }
 0659                    entries[i] = entry;
 0660                }
 0661            }
 662            else
 0663            {
 0664                foreach (HeaderEntry entry in sourceHeaders.GetEntries())
 0665                {
 0666                    ref object? storeValueRef = ref GetValueRefOrAddDefault(entry.Key);
 0667                    if (storeValueRef is null)
 0668                    {
 0669                        object sourceValue = entry.Value;
 0670                        if (sourceValue is HeaderStoreItemInfo info)
 0671                        {
 0672                            storeValueRef = CloneHeaderInfo(entry.Key, info);
 0673                        }
 674                        else
 0675                        {
 0676                            Debug.Assert(sourceValue is string);
 0677                            storeValueRef = sourceValue;
 0678                        }
 0679                    }
 0680                }
 0681            }
 0682        }
 683
 684        private static HeaderStoreItemInfo CloneHeaderInfo(HeaderDescriptor descriptor, HeaderStoreItemInfo sourceInfo)
 0685        {
 0686            lock (sourceInfo)
 0687            {
 0688                var destinationInfo = new HeaderStoreItemInfo
 0689                {
 0690                    // Always copy raw values
 0691                    RawValue = CloneStringHeaderInfoValues(sourceInfo.RawValue)
 0692                };
 693
 0694                if (descriptor.Parser == null)
 0695                {
 0696                    sourceInfo.AssertContainsNoInvalidValues();
 0697                    destinationInfo.ParsedAndInvalidValues = CloneStringHeaderInfoValues(sourceInfo.ParsedAndInvalidValu
 0698                }
 699                else
 0700                {
 701                    // We have a parser, so we also have to clone invalid values and parsed values.
 0702                    if (sourceInfo.ParsedAndInvalidValues != null)
 0703                    {
 0704                        List<object>? sourceValues = sourceInfo.ParsedAndInvalidValues as List<object>;
 0705                        if (sourceValues == null)
 0706                        {
 0707                            CloneAndAddValue(destinationInfo, sourceInfo.ParsedAndInvalidValues);
 0708                        }
 709                        else
 0710                        {
 0711                            foreach (object item in sourceValues)
 0712                            {
 0713                                CloneAndAddValue(destinationInfo, item);
 0714                            }
 0715                        }
 0716                    }
 0717                }
 718
 0719                return destinationInfo;
 720            }
 0721        }
 722
 723        private static void CloneAndAddValue(HeaderStoreItemInfo destinationInfo, object source)
 0724        {
 725            // We only have one value. Clone it and assign it to the store.
 0726            if (source is ICloneable cloneableValue)
 0727            {
 0728                Debug.Assert(source is not InvalidValue);
 0729                AddParsedValue(destinationInfo, cloneableValue.Clone());
 0730            }
 731            else
 0732            {
 733                // If it doesn't implement ICloneable, it's a value type or an immutable type like String/Uri.
 0734                AddParsedValue(destinationInfo, source);
 0735            }
 0736        }
 737
 738        [return: NotNullIfNotNull(nameof(source))]
 739        private static object? CloneStringHeaderInfoValues(object? source)
 0740        {
 0741            if (source == null)
 0742            {
 0743                return null;
 744            }
 745
 0746            List<object>? sourceValues = source as List<object>;
 0747            if (sourceValues == null)
 0748            {
 749                // If we just have one value, return the reference to the string (strings are immutable so it's OK
 750                // to use the reference).
 0751                return source;
 752            }
 753            else
 0754            {
 755                // If we have a list of strings, create a new list and copy all strings to the new list.
 0756                return new List<object>(sourceValues);
 757            }
 0758        }
 759
 760        private HeaderStoreItemInfo GetOrCreateHeaderInfo(HeaderDescriptor descriptor)
 0761        {
 0762            if (TryGetAndParseHeaderInfo(descriptor, out HeaderStoreItemInfo? info))
 0763            {
 0764                return info;
 765            }
 766            else
 0767            {
 0768                return CreateAndAddHeaderToStore(descriptor);
 769            }
 0770        }
 771
 772        private HeaderStoreItemInfo CreateAndAddHeaderToStore(HeaderDescriptor descriptor)
 0773        {
 0774            Debug.Assert(!Contains(descriptor));
 775
 776            // If we don't have the header in the store yet, add it now.
 0777            HeaderStoreItemInfo result = new HeaderStoreItemInfo();
 778
 779            // If the descriptor header type is in _treatAsCustomHeaderTypes, it must be converted to a custom header be
 0780            Debug.Assert((descriptor.HeaderType & _treatAsCustomHeaderTypes) == 0);
 781
 0782            AddEntryToStore(new HeaderEntry(descriptor, result));
 783
 0784            return result;
 0785        }
 786
 787        internal bool TryGetHeaderValue(HeaderDescriptor descriptor, [NotNullWhen(true)] out object? value)
 0788        {
 0789            ref object storeValueRef = ref GetValueRefOrNullRef(descriptor);
 0790            if (Unsafe.IsNullRef(ref storeValueRef))
 0791            {
 0792                value = null;
 0793                return false;
 794            }
 795            else
 0796            {
 0797                value = storeValueRef;
 0798                return true;
 799            }
 0800        }
 801
 802        private bool TryGetAndParseHeaderInfo(HeaderDescriptor key, [NotNullWhen(true)] out HeaderStoreItemInfo? info)
 24858803        {
 24858804            ref object storeValueRef = ref GetValueRefOrNullRef(key);
 24858805            if (!Unsafe.IsNullRef(ref storeValueRef))
 8541806            {
 8541807                object value = storeValueRef;
 808
 8541809                info = value is HeaderStoreItemInfo hsi
 8541810                    ? hsi
 8541811                    : ReplaceWithHeaderStoreItemInfo(ref storeValueRef, value);
 812
 8541813                ParseRawHeaderValues(key, info);
 8541814                return true;
 815            }
 816
 16317817            info = null;
 16317818            return false;
 24858819        }
 820
 821        /// <summary>
 822        /// Replaces <paramref name="storeValueRef"/> with a new <see cref="HeaderStoreItemInfo"/>,
 823        /// or returns the existing <see cref="HeaderStoreItemInfo"/> if a different thread beat us to it.
 824        /// </summary>
 825        /// <remarks>
 826        /// This helper should be used any time we're upgrading a storage slot from an unparsed string to a HeaderStoreI
 827        /// Concurrent writes to the header collection are UB, so we don't need to worry about race conditions when doin
 828        /// </remarks>
 829        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 830        private static HeaderStoreItemInfo ReplaceWithHeaderStoreItemInfo(ref object storeValueRef, object value)
 16199831        {
 16199832            Debug.Assert(value is string);
 833
 16199834            var info = new HeaderStoreItemInfo() { RawValue = value };
 16199835            object previousValue = Interlocked.CompareExchange(ref storeValueRef, info, value);
 836
 16199837            if (ReferenceEquals(previousValue, value))
 16199838            {
 16199839                return info;
 840            }
 841
 842            // Rare race condition: Another thread replaced the value with a HeaderStoreItemInfo.
 0843            return (HeaderStoreItemInfo)previousValue;
 16199844        }
 845
 846        private static void ParseRawHeaderValues(HeaderDescriptor descriptor, HeaderStoreItemInfo info)
 38484847        {
 848            // Unlike TryGetHeaderInfo() this method tries to parse all non-validated header values (if any)
 849            // before returning to the caller.
 38484850            lock (info)
 38484851            {
 38484852                Debug.Assert(!info.IsEmpty);
 38484853                if (info.RawValue != null)
 21402854                {
 21402855                    if (info.RawValue is List<string> rawValues)
 5203856                    {
 36421857                        foreach (string rawValue in rawValues)
 10406858                        {
 10406859                            ParseSingleRawHeaderValue(info, descriptor, rawValue);
 10406860                        }
 5203861                    }
 862                    else
 16199863                    {
 16199864                        string? rawValue = info.RawValue as string;
 16199865                        Debug.Assert(rawValue is not null);
 16199866                        ParseSingleRawHeaderValue(info, descriptor, rawValue);
 16199867                    }
 868
 869                    // At this point all values are either in info.ParsedValue, info.InvalidValue. Reset RawValue.
 21402870                    Debug.Assert(info.ParsedAndInvalidValues is not null);
 21402871                    info.RawValue = null;
 21402872                }
 38484873            }
 38484874        }
 875
 876        private static void ParseSingleRawHeaderValue(HeaderStoreItemInfo info, HeaderDescriptor descriptor, string rawV
 26605877        {
 26605878            Debug.Assert(Monitor.IsEntered(info));
 26605879            if (descriptor.Parser == null)
 10406880            {
 10406881                if (HttpRuleParser.ContainsNewLineOrNull(rawValue))
 0882                {
 0883                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, SR.Format(SR.net_http_log_headers_no_
 0884                    AddInvalidValue(info, rawValue);
 0885                }
 886                else
 10406887                {
 10406888                    AddParsedValue(info, rawValue);
 10406889                }
 10406890            }
 891            else
 16199892            {
 16199893                if (!TryParseAndAddRawHeaderValue(descriptor, info, rawValue, true))
 7540894                {
 7540895                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Log.HeadersInvalidValue(descriptor.Name, rawValue
 7540896                }
 16199897            }
 26605898        }
 899
 900        // See Add(name, string)
 901        internal bool TryParseAndAddValue(HeaderDescriptor descriptor, string? value)
 0902        {
 903            // We don't use GetOrCreateHeaderInfo() here, since this would create a new header in the store. If parsing
 904            // the value then throws, we would have to remove the header from the store again. So just get a
 905            // HeaderStoreItemInfo object and try to parse the value. If it works, we'll add the header.
 906            HeaderStoreItemInfo info;
 907            bool addToStore;
 0908            PrepareHeaderInfoForAdd(descriptor, out info, out addToStore);
 909
 0910            bool result = TryParseAndAddRawHeaderValue(descriptor, info, value, false);
 911
 0912            if (result && addToStore && (info.ParsedAndInvalidValues != null))
 0913            {
 0914                info.AssertContainsNoInvalidValues();
 915                // If we get here, then the value could be parsed correctly. If we created a new HeaderStoreItemInfo, ad
 916                // it to the store if we added at least one value.
 0917                Debug.Assert(!Contains(descriptor));
 0918                AddEntryToStore(new HeaderEntry(descriptor, info));
 0919            }
 920
 0921            return result;
 0922        }
 923
 924        // See ParseAndAddValue
 925        private static bool TryParseAndAddRawHeaderValue(HeaderDescriptor descriptor, HeaderStoreItemInfo info, string? 
 16199926        {
 16199927            Debug.Assert(info != null);
 16199928            Debug.Assert(descriptor.Parser != null);
 929
 930            // Values are added as 'invalid' if we either can't parse the value OR if we already have a value
 931            // and the current header doesn't support multiple values: e.g. trying to add a date/time value
 932            // to the 'Date' header if we already have a date/time value will result in the second value being
 933            // added to the 'invalid' header values.
 16199934            if (!info.CanAddParsedValue(descriptor.Parser))
 0935            {
 0936                if (addWhenInvalid)
 0937                {
 0938                    AddInvalidValue(info, value ?? string.Empty);
 0939                }
 0940                return false;
 941            }
 942
 16199943            int index = 0;
 944
 16199945            if (descriptor.Parser.TryParseValue(value, info.ParsedAndInvalidValues, ref index, out object? parsedValue))
 10093946            {
 947                // The raw string only represented one value (which was successfully parsed). Add the value and return.
 10093948                if ((value == null) || (index == value.Length))
 2925949                {
 2925950                    if (parsedValue != null)
 2807951                    {
 2807952                        AddParsedValue(info, parsedValue);
 2807953                    }
 118954                    else if (addWhenInvalid && info.ParsedAndInvalidValues is null)
 118955                    {
 118956                        AddInvalidValue(info, value ?? string.Empty);
 118957                    }
 2925958                    return true;
 959                }
 7168960                Debug.Assert(index < value.Length, "Parser must return an index value within the string length.");
 961
 962                // If we successfully parsed a value, but there are more left to read, store the results in a temp
 963                // list. Only when all values are parsed successfully write the list to the store.
 7168964                List<object> parsedValues = new List<object>();
 7168965                if (parsedValue != null)
 7168966                {
 7168967                    parsedValues.Add(parsedValue);
 7168968                }
 969
 261837970                while (index < value.Length)
 256103971                {
 256103972                    if (descriptor.Parser.TryParseValue(value, info.ParsedAndInvalidValues, ref index, out parsedValue))
 254669973                    {
 254669974                        if (parsedValue != null)
 254669975                        {
 254669976                            parsedValues.Add(parsedValue);
 254669977                        }
 254669978                    }
 979                    else
 1434980                    {
 1434981                        if (addWhenInvalid)
 1434982                        {
 1434983                            AddInvalidValue(info, value);
 1434984                        }
 1434985                        return false;
 986                    }
 254669987                }
 988
 989                // All values were parsed correctly. Copy results to the store.
 499862990                foreach (object item in parsedValues)
 241330991                {
 241330992                    AddParsedValue(info, item);
 241330993                }
 994
 5734995                if (parsedValues.Count == 0 && addWhenInvalid && info.ParsedAndInvalidValues is null)
 0996                {
 0997                    AddInvalidValue(info, value);
 0998                }
 999
 57341000                return true;
 1001            }
 1002
 61061003            Debug.Assert(value != null);
 61061004            if (addWhenInvalid)
 61061005            {
 61061006                AddInvalidValue(info, value ?? string.Empty);
 61061007            }
 61061008            return false;
 161991009        }
 1010
 1011        private static void AddParsedValue(HeaderStoreItemInfo info, object value)
 7407321012        {
 7407321013            Debug.Assert(!(value is List<object>),
 7407321014                "Header value types must not derive from List<object> since this type is used internally to store " +
 7407321015                "lists of values. So we would not be able to distinguish between a single value and a list of values.");
 1016
 7407321017            AddValueToStoreValue<object>(value, ref info.ParsedAndInvalidValues);
 7407321018        }
 1019
 1020        private static void AddInvalidValue(HeaderStoreItemInfo info, string value)
 76581021        {
 76581022            AddValueToStoreValue<object>(new InvalidValue(value), ref info.ParsedAndInvalidValues);
 76581023        }
 1024
 1025        private static void AddRawValue(HeaderStoreItemInfo info, string value)
 52031026        {
 52031027            AddValueToStoreValue<string>(value, ref info.RawValue);
 52031028        }
 1029
 1030        private static void AddValueToStoreValue<T>(T value, ref object? currentStoreValue) where T : class
 7535931031        {
 1032            // If there is no value set yet, then add current item as value (we don't create a list
 1033            // if not required). If 'info.Value' is already assigned then make sure 'info.Value' is a
 1034            // List<T> and append 'item' to the list.
 7535931035            if (currentStoreValue == null)
 299431036            {
 299431037                currentStoreValue = value;
 299431038            }
 1039            else
 7236501040            {
 7236501041                List<T>? storeValues = currentStoreValue as List<T>;
 1042
 7236501043                if (storeValues == null)
 225961044                {
 225961045                    storeValues = new List<T>(2);
 225961046                    Debug.Assert(currentStoreValue is T);
 225961047                    storeValues.Add((T)currentStoreValue);
 225961048                    currentStoreValue = storeValues;
 225961049                }
 7236501050                Debug.Assert(value is T);
 7236501051                storeValues.Add((T)value);
 7236501052            }
 7535931053        }
 1054
 1055        internal object? GetSingleParsedValue(HeaderDescriptor descriptor)
 01056        {
 01057            if (!TryGetAndParseHeaderInfo(descriptor, out HeaderStoreItemInfo? info))
 01058            {
 01059                return null;
 1060            }
 1061
 01062            return info.GetSingleParsedValue();
 01063        }
 1064
 1065        internal object? GetParsedAndInvalidValues(HeaderDescriptor descriptor)
 01066        {
 01067            if (!TryGetAndParseHeaderInfo(descriptor, out HeaderStoreItemInfo? info))
 01068            {
 01069                return null;
 1070            }
 1071
 01072            return info.ParsedAndInvalidValues;
 01073        }
 1074
 198461075        internal virtual bool IsAllowedHeaderName(HeaderDescriptor descriptor) => true;
 1076
 1077        private void CheckIsAllowedHeaderName(HeaderDescriptor descriptor)
 389691078        {
 389691079            if (!IsAllowedHeaderName(descriptor))
 01080            {
 01081                throw new InvalidOperationException(SR.Format(SR.net_http_headers_not_allowed_header_name, descriptor.Na
 1082            }
 389691083        }
 1084
 1085        private void PrepareHeaderInfoForAdd(HeaderDescriptor descriptor, out HeaderStoreItemInfo info, out bool addToSt
 248581086        {
 248581087            CheckIsAllowedHeaderName(descriptor);
 1088
 248581089            addToStore = false;
 248581090            if (!TryGetAndParseHeaderInfo(descriptor, out info!))
 163171091            {
 163171092                info = new HeaderStoreItemInfo();
 163171093                addToStore = true;
 163171094            }
 248581095        }
 1096
 1097        private static void ParseAndAddValue(HeaderDescriptor descriptor, HeaderStoreItemInfo info, string? value)
 248581098        {
 248581099            Debug.Assert(info != null);
 248581100            Debug.Assert(descriptor.Parser != null);
 1101
 1102            // If the header only supports 1 value, we can add the current value only if there is no
 1103            // value already set.
 248581104            if (!info.CanAddParsedValue(descriptor.Parser))
 4191105            {
 4191106                throw new FormatException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_heade
 1107            }
 1108
 244391109            int index = 0;
 244391110            object parsedValue = descriptor.Parser.ParseValue(value, info.ParsedAndInvalidValues, ref index);
 1111
 1112            // The raw string only represented one value (which was successfully parsed). Add the value and return.
 1113            // If value is null we still have to first call ParseValue() to allow the parser to decide whether null is
 1114            // a valid value. If it is (i.e. no exception thrown), we set the parsed value (if any) and return.
 183331115            if ((value == null) || (index == value.Length))
 54311116            {
 1117                // If the returned value is null, then it means the header accepts empty values. i.e. we don't throw
 1118                // but we don't add 'null' to the store either.
 54311119                if (parsedValue != null)
 35291120                {
 35291121                    AddParsedValue(info, parsedValue);
 35291122                }
 54311123                return;
 1124            }
 129021125            Debug.Assert(index < value.Length, "Parser must return an index value within the string length.");
 1126
 1127            // If we successfully parsed a value, but there are more left to read, store the results in a temp
 1128            // list. Only when all values are parsed successfully write the list to the store.
 129021129            List<object> parsedValues = new List<object>();
 129021130            if (parsedValue != null)
 129021131            {
 129021132                parsedValues.Add(parsedValue);
 129021133            }
 1134
 5031671135            while (index < value.Length)
 4916991136            {
 4916991137                parsedValue = descriptor.Parser.ParseValue(value, info.ParsedAndInvalidValues, ref index);
 4902651138                if (parsedValue != null)
 4902651139                {
 4902651140                    parsedValues.Add(parsedValue);
 4902651141                }
 4902651142            }
 1143
 1144            // All values were parsed correctly. Copy results to the store.
 9997241145            foreach (object item in parsedValues)
 4826601146            {
 4826601147                AddParsedValue(info, item);
 4826601148            }
 168991149        }
 1150
 1151        internal HeaderDescriptor GetHeaderDescriptor(string name)
 389691152        {
 389691153            ArgumentException.ThrowIfNullOrEmpty(name);
 1154
 389691155            if (!HeaderDescriptor.TryGet(name, out HeaderDescriptor descriptor))
 01156            {
 01157                throw new FormatException(SR.Format(SR.net_http_headers_invalid_header_name, name));
 1158            }
 1159
 389691160            if ((descriptor.HeaderType & _allowedHeaderTypes) != 0)
 261191161            {
 261191162                return descriptor;
 1163            }
 128501164            else if ((descriptor.HeaderType & _treatAsCustomHeaderTypes) != 0)
 128501165            {
 128501166                return descriptor.AsCustomHeader();
 1167            }
 1168
 01169            throw new InvalidOperationException(SR.Format(SR.net_http_headers_not_allowed_header_name, name));
 389691170        }
 1171
 1172        internal bool TryGetHeaderDescriptor(string name, out HeaderDescriptor descriptor)
 376621173        {
 376621174            if (string.IsNullOrEmpty(name))
 01175            {
 01176                descriptor = default;
 01177                return false;
 1178            }
 1179
 376621180            if (HeaderDescriptor.TryGet(name, out descriptor))
 376621181            {
 376621182                HttpHeaderType headerType = descriptor.HeaderType;
 1183
 376621184                if ((headerType & _allowedHeaderTypes) != 0)
 170761185                {
 170761186                    return true;
 1187                }
 1188
 205861189                if ((headerType & _treatAsCustomHeaderTypes) != 0)
 80311190                {
 80311191                    descriptor = descriptor.AsCustomHeader();
 80311192                    return true;
 1193                }
 125551194            }
 1195
 125551196            return false;
 376621197        }
 1198
 1199        internal static void CheckContainsNewLineOrNull(string? value)
 1236361200        {
 1236361201            if (value == null)
 995181202            {
 995181203                return;
 1204            }
 1205
 241181206            if (HttpRuleParser.ContainsNewLineOrNull(value))
 37051207            {
 37051208                throw new FormatException(SR.net_http_headers_no_newlines_no_nul);
 1209            }
 1199311210        }
 1211
 1212        internal static string[] GetStoreValuesAsStringArray(HeaderDescriptor descriptor, HeaderStoreItemInfo info)
 299431213        {
 299431214            GetStoreValuesAsStringOrStringArray(descriptor, info, out string? singleValue, out string[]? multiValue);
 299431215            Debug.Assert(singleValue is not null ^ multiValue is not null);
 299431216            return multiValue ?? new[] { singleValue! };
 299431217        }
 1218
 1219        internal static void GetStoreValuesAsStringOrStringArray(HeaderDescriptor descriptor, object sourceValues, out s
 436871220        {
 436871221            HeaderStoreItemInfo? info = sourceValues as HeaderStoreItemInfo;
 436871222            if (info is null)
 01223            {
 01224                Debug.Assert(sourceValues is string);
 01225                singleValue = (string)sourceValues;
 01226                multiValue = null;
 01227                return;
 1228            }
 1229
 436871230            lock (info)
 436871231            {
 436871232                int length = GetValueCount(info);
 1233
 1234                scoped Span<string?> values;
 436871235                singleValue = null;
 436871236                if (length == 1)
 146351237                {
 146351238                    multiValue = null;
 146351239                    values = new Span<string?>(ref singleValue);
 146351240                }
 1241                else
 290521242                {
 290521243                    Debug.Assert(length > 1, "The header should have been removed when it became empty");
 290521244                    values = (multiValue = new string[length])!;
 290521245                }
 1246
 436871247                int currentIndex = 0;
 436871248                ReadStoreValues<object?>(values, info.ParsedAndInvalidValues, descriptor.Parser, ref currentIndex);
 436871249                ReadStoreValues<string?>(values, info.RawValue, null, ref currentIndex);
 1250
 436871251                Debug.Assert(currentIndex == length);
 436871252            }
 436871253        }
 1254
 1255        internal static int GetStoreValuesIntoStringArray(HeaderDescriptor descriptor, object sourceValues, [NotNull] re
 01256        {
 01257            values ??= Array.Empty<string>();
 1258
 01259            HeaderStoreItemInfo? info = sourceValues as HeaderStoreItemInfo;
 01260            if (info is null)
 01261            {
 01262                Debug.Assert(sourceValues is string);
 1263
 01264                if (values.Length == 0)
 01265                {
 01266                    values = new string[1];
 01267                }
 1268
 01269                values[0] = (string)sourceValues;
 01270                return 1;
 1271            }
 1272
 01273            lock (info)
 01274            {
 01275                int length = GetValueCount(info);
 01276                Debug.Assert(length > 0);
 1277
 01278                if (values.Length < length)
 01279                {
 01280                    values = new string[length];
 01281                }
 1282
 01283                int currentIndex = 0;
 01284                ReadStoreValues<object?>(values!, info.ParsedAndInvalidValues, descriptor.Parser, ref currentIndex);
 01285                ReadStoreValues<string?>(values!, info.RawValue, null, ref currentIndex);
 01286                Debug.Assert(currentIndex == length);
 1287
 01288                return length;
 1289            }
 01290        }
 1291
 1292        private static int GetValueCount(HeaderStoreItemInfo info)
 436871293        {
 436871294            Debug.Assert(info != null);
 436871295            Debug.Assert(Monitor.IsEntered(info));
 1296
 436871297            return Count<object>(info.ParsedAndInvalidValues) + Count<string>(info.RawValue);
 1298
 1299            static int Count<T>(object? valueStore) =>
 873741300                valueStore is null ? 0 :
 873741301                valueStore is List<T> list ? list.Count :
 873741302                1;
 436871303        }
 1304
 1305        private static void ReadStoreValues<T>(Span<string?> values, object? storeValue, HttpHeaderParser? parser, ref i
 873741306        {
 873741307            if (storeValue != null)
 436871308            {
 436871309                List<T>? storeValues = storeValue as List<T>;
 1310
 436871311                if (storeValues == null)
 146351312                {
 146351313                    values[currentIndex] = parser == null || storeValue is InvalidValue ? storeValue.ToString() : parser
 146351314                    currentIndex++;
 146351315                }
 1316                else
 290521317                {
 25478561318                    foreach (object? item in storeValues)
 12303501319                    {
 12303501320                        Debug.Assert(item != null);
 12303501321                        values[currentIndex] = parser == null || item is InvalidValue ? item.ToString() : parser.ToStrin
 12303501322                        currentIndex++;
 12303501323                    }
 290521324                }
 436871325            }
 873741326        }
 1327
 1328        private static bool AreEqual(object value, object? storeValue, IEqualityComparer? comparer)
 01329        {
 01330            Debug.Assert(value != null);
 1331
 01332            if (comparer != null)
 01333            {
 01334                return comparer.Equals(value, storeValue);
 1335            }
 1336
 1337            // We don't have a comparer, so use the Equals() method.
 01338            return value.Equals(storeValue);
 01339        }
 1340
 1341        internal sealed class InvalidValue
 1342        {
 1343            private readonly string _value;
 1344
 76581345            public InvalidValue(string value)
 76581346            {
 76581347                Debug.Assert(value is not null);
 76581348                _value = value;
 76581349            }
 1350
 76581351            public override string ToString() => _value;
 1352        }
 1353
 1354        internal sealed class HeaderStoreItemInfo
 1355        {
 1131571356            internal HeaderStoreItemInfo() { }
 1357
 1358            internal object? RawValue;
 1359            internal object? ParsedAndInvalidValues;
 1360
 1361            public bool CanAddParsedValue(HttpHeaderParser parser)
 410571362            {
 410571363                Debug.Assert(parser != null, "There should be no reason to call CanAddValue if there is no parser for th
 1364
 1365                // If the header only supports one value, and we have already a value set, then we can't add
 1366                // another value. E.g. the 'Date' header only supports one value. We can't add multiple timestamps
 1367                // to 'Date'.
 1368                // So if this is a known header, ask the parser if it supports multiple values and check whether
 1369                // we already have a (valid or invalid) value.
 1370                // Note that we ignore the rawValue by purpose: E.g. we are parsing 2 raw values for a header only
 1371                // supporting 1 value. When the first value gets parsed, CanAddValue returns true and we add the
 1372                // parsed value to ParsedValue. When the second value is parsed, CanAddValue returns false, because
 1373                // we have already a parsed value.
 410571374                return parser.SupportsMultipleValues || ParsedAndInvalidValues is null;
 410571375            }
 1376
 1377            [Conditional("DEBUG")]
 1378            public void AssertContainsNoInvalidValues()
 85411379            {
 85411380                if (ParsedAndInvalidValues is not null)
 85411381                {
 85411382                    if (ParsedAndInvalidValues is List<object> list)
 57341383                    {
 4998621384                        foreach (object item in list)
 2413301385                        {
 2413301386                            Debug.Assert(item is not InvalidValue);
 2413301387                        }
 57341388                    }
 1389                    else
 28071390                    {
 28071391                        Debug.Assert(ParsedAndInvalidValues is not InvalidValue);
 28071392                    }
 85411393                }
 85411394            }
 1395
 1396            public object? GetSingleParsedValue()
 01397            {
 01398                if (ParsedAndInvalidValues is not null)
 01399                {
 01400                    if (ParsedAndInvalidValues is List<object> list)
 01401                    {
 01402                        AssertContainsSingleParsedValue(list);
 01403                        foreach (object item in list)
 01404                        {
 01405                            if (item is not InvalidValue)
 01406                            {
 01407                                return item;
 1408                            }
 01409                        }
 01410                    }
 1411                    else
 01412                    {
 01413                        if (ParsedAndInvalidValues is not InvalidValue)
 01414                        {
 01415                            return ParsedAndInvalidValues;
 1416                        }
 01417                    }
 01418                }
 1419
 01420                return null;
 01421            }
 1422
 1423            [Conditional("DEBUG")]
 1424            private static void AssertContainsSingleParsedValue(List<object> list)
 01425            {
 01426                int count = 0;
 01427                foreach (object item in list)
 01428                {
 01429                    if (item is not InvalidValue)
 01430                    {
 01431                        count++;
 01432                    }
 01433                }
 1434
 01435                Debug.Assert(count == 1, "Only a single parsed value should be stored for this parser");
 01436            }
 1437
 384841438            public bool IsEmpty => RawValue == null && ParsedAndInvalidValues == null;
 1439        }
 1440
 1441
 1442        #region Low-level implementation details that work with _headerStore directly
 1443
 1444        private const int InitialCapacity = 4;
 1445        internal const int ArrayThreshold = 64; // Above this threshold, header ordering will not be preserved
 1446
 1447        internal HeaderEntry[]? GetEntriesArray()
 639581448        {
 639581449            object? store = _headerStore;
 639581450            if (store is null)
 01451            {
 01452                return null;
 1453            }
 639581454            else if (store is HeaderEntry[] entries)
 639581455            {
 639581456                return entries;
 1457            }
 1458            else
 01459            {
 01460                return GetEntriesFromDictionary();
 1461            }
 1462
 1463            HeaderEntry[] GetEntriesFromDictionary()
 01464            {
 01465                var dictionary = (Dictionary<HeaderDescriptor, object>)_headerStore!;
 01466                var entries = new HeaderEntry[dictionary.Count];
 01467                int i = 0;
 01468                foreach (KeyValuePair<HeaderDescriptor, object> entry in dictionary)
 01469                {
 01470                    entries[i++] = new HeaderEntry
 01471                    {
 01472                        Key = entry.Key,
 01473                        Value = entry.Value
 01474                    };
 01475                }
 01476                return entries;
 01477            }
 639581478        }
 1479
 1480        internal ReadOnlySpan<HeaderEntry> GetEntries()
 01481        {
 01482            return new ReadOnlySpan<HeaderEntry>(GetEntriesArray(), 0, _count);
 01483        }
 1484
 251071485        internal int Count => _count;
 1486
 161991487        private bool EntriesAreLiveView => _headerStore is HeaderEntry[];
 1488
 1489        private ref object GetValueRefOrNullRef(HeaderDescriptor key)
 419401490        {
 419401491            ref object valueRef = ref Unsafe.NullRef<object>();
 1492
 419401493            object? store = _headerStore;
 419401494            if (store is HeaderEntry[] entries)
 419401495            {
 838801496                for (int i = 0; i < _count && i < entries.Length; i++)
 85411497                {
 85411498                    if (key.Equals(entries[i].Key))
 85411499                    {
 85411500                        valueRef = ref entries[i].Value;
 85411501                        break;
 1502                    }
 01503                }
 419401504            }
 01505            else if (store is not null)
 01506            {
 01507                valueRef = ref CollectionsMarshal.GetValueRefOrNullRef((Dictionary<HeaderDescriptor, object>)store, key)
 01508            }
 1509
 419401510            return ref valueRef;
 419401511        }
 1512
 1513        private ref object? GetValueRefOrAddDefault(HeaderDescriptor key)
 355131514        {
 355131515            object? store = _headerStore;
 355131516            if (store is HeaderEntry[] entries)
 355101517            {
 710201518                for (int i = 0; i < _count && i < entries.Length; i++)
 52031519                {
 52031520                    if (key.Equals(entries[i].Key))
 52031521                    {
 52031522                        return ref entries[i].Value!;
 1523                    }
 01524                }
 1525
 303071526                int count = _count;
 303071527                _count++;
 303071528                if ((uint)count < (uint)entries.Length)
 303071529                {
 303071530                    entries[count].Key = key;
 303071531                    return ref entries[count].Value!;
 1532                }
 1533
 01534                return ref GrowEntriesAndAddDefault(key);
 1535            }
 31536            else if (store is null)
 31537            {
 31538                _count++;
 31539                entries = new HeaderEntry[InitialCapacity];
 31540                _headerStore = entries;
 31541                entries[0].Key = key;
 31542                return ref entries[0].Value!;
 1543            }
 1544            else
 01545            {
 01546                return ref DictionaryGetValueRefOrAddDefault(key);
 1547            }
 1548
 1549            ref object? GrowEntriesAndAddDefault(HeaderDescriptor key)
 01550            {
 01551                var entries = (HeaderEntry[])_headerStore!;
 01552                if (entries.Length == ArrayThreshold)
 01553                {
 01554                    return ref ConvertToDictionaryAndAddDefault(key);
 1555                }
 1556                else
 01557                {
 01558                    Array.Resize(ref entries, entries.Length << 1);
 01559                    _headerStore = entries;
 01560                    ref HeaderEntry firstNewEntry = ref entries[entries.Length >> 1];
 01561                    firstNewEntry.Key = key;
 01562                    return ref firstNewEntry.Value!;
 1563                }
 01564            }
 1565
 1566            ref object? ConvertToDictionaryAndAddDefault(HeaderDescriptor key)
 01567            {
 01568                var entries = (HeaderEntry[])_headerStore!;
 01569                var dictionary = new Dictionary<HeaderDescriptor, object>(ArrayThreshold);
 01570                _headerStore = dictionary;
 01571                foreach (HeaderEntry entry in entries)
 01572                {
 01573                    dictionary.Add(entry.Key, entry.Value);
 01574                }
 01575                Debug.Assert(dictionary.Count == _count - 1);
 01576                return ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out _);
 01577            }
 1578
 1579            ref object? DictionaryGetValueRefOrAddDefault(HeaderDescriptor key)
 01580            {
 01581                var dictionary = (Dictionary<HeaderDescriptor, object>)_headerStore!;
 01582                ref object? value = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out _);
 01583                if (value is null)
 01584                {
 01585                    _count++;
 01586                }
 01587                return ref value;
 01588            }
 355131589        }
 1590
 1591        private void AddEntryToStore(HeaderEntry entry)
 85411592        {
 85411593            Debug.Assert(!Contains(entry.Key));
 1594
 85411595            if (_headerStore is HeaderEntry[] entries)
 85411596            {
 85411597                int count = _count;
 85411598                if ((uint)count < (uint)entries.Length)
 85411599                {
 85411600                    entries[count] = entry;
 85411601                    _count++;
 85411602                    return;
 1603                }
 01604            }
 1605
 01606            GetValueRefOrAddDefault(entry.Key) = entry.Value;
 85411607        }
 1608
 1609        internal bool Contains(HeaderDescriptor key)
 170821610        {
 170821611            return !Unsafe.IsNullRef(ref GetValueRefOrNullRef(key));
 170821612        }
 1613
 1614        public void Clear()
 627691615        {
 627691616            if (_headerStore is HeaderEntry[] entries)
 627221617            {
 627221618                Array.Clear(entries, 0, _count);
 627221619            }
 1620            else
 471621            {
 471622                _headerStore = null;
 471623            }
 627691624            _count = 0;
 627691625        }
 1626
 1627        internal bool Remove(HeaderDescriptor key)
 01628        {
 01629            bool removed = false;
 1630
 01631            object? store = _headerStore;
 01632            if (store is HeaderEntry[] entries)
 01633            {
 01634                for (int i = 0; i < _count && i < entries.Length; i++)
 01635                {
 01636                    if (key.Equals(entries[i].Key))
 01637                    {
 01638                        while (i + 1 < _count && (uint)(i + 1) < (uint)entries.Length)
 01639                        {
 01640                            entries[i] = entries[i + 1];
 01641                            i++;
 01642                        }
 01643                        entries[i] = default;
 01644                        removed = true;
 01645                        break;
 1646                    }
 01647                }
 01648            }
 01649            else if (store is not null)
 01650            {
 01651                removed = ((Dictionary<HeaderDescriptor, object>)store).Remove(key);
 01652            }
 1653
 01654            if (removed)
 01655            {
 01656                _count--;
 01657            }
 1658
 01659            return removed;
 01660        }
 1661
 1662        #endregion // _headerStore implementation
 1663    }
 1664}

Methods/Properties

.ctor()
.ctor(System.Net.Http.Headers.HttpHeaderType,System.Net.Http.Headers.HttpHeaderType)
NonValidated()
Add(System.String,System.String)
Add(System.Net.Http.Headers.HeaderDescriptor,System.String)
Add(System.String,System.Collections.Generic.IEnumerable`1<System.String>)
Add(System.Net.Http.Headers.HeaderDescriptor,System.Collections.Generic.IEnumerable`1<System.String>)
TryAddWithoutValidation(System.String,System.String)
TryAddWithoutValidation(System.Net.Http.Headers.HeaderDescriptor,System.String)
TryAddWithoutValidation(System.String,System.Collections.Generic.IEnumerable`1<System.String>)
TryAddWithoutValidation(System.Net.Http.Headers.HeaderDescriptor,System.Collections.Generic.IEnumerable`1<System.String>)
GetValues(System.String)
GetValues(System.Net.Http.Headers.HeaderDescriptor)
TryGetValues(System.String,System.Collections.Generic.IEnumerable`1<System.String>&)
TryGetValues(System.Net.Http.Headers.HeaderDescriptor,System.Collections.Generic.IEnumerable`1<System.String>&)
Contains(System.String)
ToString()
Dump(System.Text.ValueStringBuilder&,System.Boolean)
GetHeaderString(System.Net.Http.Headers.HeaderDescriptor)
GetEnumerator()
GetEnumeratorCore()
System.Collections.IEnumerable.GetEnumerator()
AddParsedValue(System.Net.Http.Headers.HeaderDescriptor,System.Object)
SetParsedValue(System.Net.Http.Headers.HeaderDescriptor,System.Object)
SetOrRemoveParsedValue(System.Net.Http.Headers.HeaderDescriptor,System.Object)
Remove(System.String)
RemoveParsedValue(System.Net.Http.Headers.HeaderDescriptor,System.Object,System.Boolean)
ContainsParsedValue(System.Net.Http.Headers.HeaderDescriptor,System.Object)
AddHeaders(System.Net.Http.Headers.HttpHeaders)
CloneHeaderInfo(System.Net.Http.Headers.HeaderDescriptor,System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo)
CloneAndAddValue(System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo,System.Object)
CloneStringHeaderInfoValues(System.Object)
GetOrCreateHeaderInfo(System.Net.Http.Headers.HeaderDescriptor)
CreateAndAddHeaderToStore(System.Net.Http.Headers.HeaderDescriptor)
TryGetHeaderValue(System.Net.Http.Headers.HeaderDescriptor,System.Object&)
TryGetAndParseHeaderInfo(System.Net.Http.Headers.HeaderDescriptor,System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo&)
ReplaceWithHeaderStoreItemInfo(System.Object&,System.Object)
ParseRawHeaderValues(System.Net.Http.Headers.HeaderDescriptor,System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo)
ParseSingleRawHeaderValue(System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo,System.Net.Http.Headers.HeaderDescriptor,System.String)
TryParseAndAddValue(System.Net.Http.Headers.HeaderDescriptor,System.String)
TryParseAndAddRawHeaderValue(System.Net.Http.Headers.HeaderDescriptor,System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo,System.String,System.Boolean)
AddParsedValue(System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo,System.Object)
AddInvalidValue(System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo,System.String)
AddRawValue(System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo,System.String)
AddValueToStoreValue(T,System.Object&)
GetSingleParsedValue(System.Net.Http.Headers.HeaderDescriptor)
GetParsedAndInvalidValues(System.Net.Http.Headers.HeaderDescriptor)
IsAllowedHeaderName(System.Net.Http.Headers.HeaderDescriptor)
CheckIsAllowedHeaderName(System.Net.Http.Headers.HeaderDescriptor)
PrepareHeaderInfoForAdd(System.Net.Http.Headers.HeaderDescriptor,System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo&,System.Boolean&)
ParseAndAddValue(System.Net.Http.Headers.HeaderDescriptor,System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo,System.String)
GetHeaderDescriptor(System.String)
TryGetHeaderDescriptor(System.String,System.Net.Http.Headers.HeaderDescriptor&)
CheckContainsNewLineOrNull(System.String)
GetStoreValuesAsStringArray(System.Net.Http.Headers.HeaderDescriptor,System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo)
GetStoreValuesAsStringOrStringArray(System.Net.Http.Headers.HeaderDescriptor,System.Object,System.String&,System.String[]&)
GetStoreValuesIntoStringArray(System.Net.Http.Headers.HeaderDescriptor,System.Object,System.String[]&)
GetValueCount(System.Net.Http.Headers.HttpHeaders/HeaderStoreItemInfo)
Count(System.Object)
ReadStoreValues(System.Span`1<System.String>,System.Object,System.Net.Http.Headers.HttpHeaderParser,System.Int32&)
AreEqual(System.Object,System.Object,System.Collections.IEqualityComparer)
.ctor(System.String)
ToString()
.ctor()
CanAddParsedValue(System.Net.Http.Headers.HttpHeaderParser)
AssertContainsNoInvalidValues()
GetSingleParsedValue()
AssertContainsSingleParsedValue(System.Collections.Generic.List`1<System.Object>)
IsEmpty()
GetEntriesArray()
GetEntriesFromDictionary()
GetEntries()
Count()
EntriesAreLiveView()
GetValueRefOrNullRef(System.Net.Http.Headers.HeaderDescriptor)
GetValueRefOrAddDefault(System.Net.Http.Headers.HeaderDescriptor)
GrowEntriesAndAddDefault(System.Net.Http.Headers.HeaderDescriptor)
ConvertToDictionaryAndAddDefault(System.Net.Http.Headers.HeaderDescriptor)
DictionaryGetValueRefOrAddDefault(System.Net.Http.Headers.HeaderDescriptor)
AddEntryToStore(System.Net.Http.Headers.HeaderEntry)
Contains(System.Net.Http.Headers.HeaderDescriptor)
Clear()
Remove(System.Net.Http.Headers.HeaderDescriptor)