< 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: 617
Coverable lines: 1061
Total lines: 1668
Line coverage: 41.8%
Branch coverage
36%
Covered branches: 149
Total branches: 406
Branch coverage: 36.6%
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(...)66.66%66100%
TryAddWithoutValidation(...)0%220%
TryAddWithoutValidation(...)0%24240%
GetValues(...)100%110%
GetValues(...)0%220%
TryGetValues(...)0%220%
TryGetValues(...)0%660%
Contains(...)0%220%
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(...)0%220%
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(...)60%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(...)81.25%1616100%
GetHeaderDescriptor(...)66.66%6676.92%
TryGetHeaderDescriptor(...)75%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()83.33%66100%
GetSingleParsedValue()0%10100%
AssertContainsSingleParsedValue(...)0%440%
GetEntriesArray()50%4463.63%
GetEntriesFromDictionary()0%220%
GetEntries()100%110%
GetValueRefOrNullRef(...)60%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
 986575        public HttpHeadersNonValidated NonValidated => new HttpHeadersNonValidated(this);
 76
 1556677        public void Add(string name, string? value) => Add(GetHeaderDescriptor(name), value);
 78
 79        internal void Add(HeaderDescriptor descriptor, string? value)
 1556680        {
 1556681            if (descriptor.Parser is null)
 722882            {
 83                // If the header has no parser, we only have to check for new lines or null.
 722884                CheckIsAllowedHeaderName(descriptor);
 722885                CheckContainsNewLineOrNull(value);
 633086                TryAddWithoutValidation(descriptor, value);
 633087                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.
 833893            PrepareHeaderInfoForAdd(descriptor, out HeaderStoreItemInfo info, out bool addToStore);
 833894            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.
 499898            if (addToStore && (info.ParsedAndInvalidValues != null))
 242399            {
 2423100                info.AssertContainsNoInvalidValues();
 2423101                Debug.Assert(!Contains(descriptor));
 2423102                AddEntryToStore(new HeaderEntry(descriptor, info));
 2423103            }
 11328104        }
 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) =>
 14688161            TryGetHeaderDescriptor(name, out HeaderDescriptor descriptor) &&
 14688162            TryAddWithoutValidation(descriptor, value);
 163
 164        internal bool TryAddWithoutValidation(HeaderDescriptor descriptor, string? value)
 16195165        {
 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: ,".
 16195169            value ??= string.Empty;
 170
 16195171            ref object? storeValueRef = ref GetValueRefOrAddDefault(descriptor);
 16195172            object? currentValue = storeValueRef;
 173
 16195174            if (currentValue is null)
 13030175            {
 13030176                storeValueRef = value;
 13030177            }
 178            else
 3165179            {
 3165180                if (currentValue is not HeaderStoreItemInfo info)
 3165181                {
 182                    // The header store contained a single raw string value, so promote it
 183                    // to being a HeaderStoreItemInfo and add to it.
 3165184                    Debug.Assert(currentValue is string);
 3165185                    storeValueRef = info = new HeaderStoreItemInfo() { RawValue = currentValue };
 3165186                }
 187
 3165188                AddRawValue(info, value);
 3165189            }
 190
 16195191            return true;
 16195192        }
 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
 312        public bool Contains(string name) =>
 0313            TryGetHeaderDescriptor(name, out HeaderDescriptor descriptor) &&
 0314            Contains(descriptor);
 315
 316        public override string ToString()
 0317        {
 0318            var vsb = new ValueStringBuilder(stackalloc char[512]);
 0319            Dump(ref vsb, indentLines: false);
 0320            return vsb.ToString();
 0321        }
 322
 323        internal void Dump(ref ValueStringBuilder builder, bool indentLines)
 0324        {
 325            // Return all headers as string similar to:
 326            // HeaderName1: Value1, Value2
 327            // HeaderName2: Value1
 328            // ...
 329
 0330            foreach (HeaderEntry entry in GetEntries())
 0331            {
 0332                if (indentLines)
 0333                {
 0334                    builder.Append("  ");
 0335                }
 336
 0337                builder.Append(entry.Key.Name);
 0338                builder.Append(": ");
 339
 0340                GetStoreValuesAsStringOrStringArray(entry.Key, entry.Value, out string? singleValue, out string[]? multi
 0341                Debug.Assert(singleValue is not null ^ multiValue is not null);
 342
 0343                if (singleValue is not null)
 0344                {
 0345                    builder.Append(singleValue);
 0346                }
 347                else
 0348                {
 349                    // Note that if we get multiple values for a header that doesn't support multiple values, we'll
 350                    // just separate the values using a comma (default separator).
 0351                    string separator = entry.Key.Separator;
 352
 0353                    Debug.Assert(multiValue is not null && multiValue.Length > 0);
 0354                    builder.Append(multiValue[0]);
 0355                    for (int i = 1; i < multiValue.Length; i++)
 0356                    {
 0357                        builder.Append(separator);
 0358                        builder.Append(multiValue[i]);
 0359                    }
 0360                }
 361
 0362                builder.Append(Environment.NewLine);
 0363            }
 0364        }
 365
 366        internal string GetHeaderString(HeaderDescriptor descriptor)
 0367        {
 0368            if (TryGetHeaderValue(descriptor, out object? info))
 0369            {
 0370                GetStoreValuesAsStringOrStringArray(descriptor, info, out string? singleValue, out string[]? multiValue)
 0371                Debug.Assert(singleValue is not null ^ multiValue is not null);
 372
 0373                if (singleValue is not null)
 0374                {
 0375                    return singleValue;
 376                }
 377
 378                // Note that if we get multiple values for a header that doesn't support multiple values, we'll
 379                // just separate the values using a comma (default separator).
 0380                return string.Join(descriptor.Separator, multiValue!);
 381            }
 382
 0383            return string.Empty;
 0384        }
 385
 386        #region IEnumerable<KeyValuePair<string, IEnumerable<string>>> Members
 387
 19730388        public IEnumerator<KeyValuePair<string, IEnumerable<string>>> GetEnumerator() => _count == 0 ?
 19730389                ((IEnumerable<KeyValuePair<string, IEnumerable<string>>>)Array.Empty<KeyValuePair<string, IEnumerable<st
 19730390                GetEnumeratorCore();
 391
 392        private IEnumerator<KeyValuePair<string, IEnumerable<string>>> GetEnumeratorCore()
 15453393        {
 15453394            Debug.Assert(_headerStore is not null);
 395
 15453396            HeaderEntry[]? entries = GetEntriesArray();
 15453397            Debug.Assert(_count != 0 && entries is not null, "Caller should have validated the collection is not empty")
 398
 61812399            for (int i = 0; i < _count; i++)
 15453400            {
 15453401                HeaderEntry entry = entries[i];
 402
 15453403                if (entry.Value is not HeaderStoreItemInfo info)
 9865404                {
 9865405                    if (entry.Key.Parser is null)
 4063406                    {
 407                        // This is a custom header without a known parser, so unparsed values won't change.
 408                        // Avoid allocating the HeaderStoreItemInfo and just return the raw value as-is.
 4063409                        yield return new KeyValuePair<string, IEnumerable<string>>(entry.Key.Name, new string[] { (strin
 4063410                        continue;
 411                    }
 412
 413                    // To retain consistent semantics, we need to upgrade a raw string to a HeaderStoreItemInfo
 414                    // during enumeration so that we can parse the raw value in order to a) return
 415                    // the correct set of parsed values, and b) update the instance for subsequent enumerations
 416                    // to reflect that parsing.
 5802417                    ref object storeValueRef = ref EntriesAreLiveView
 5802418                        ? ref entries[i].Value
 5802419                        : ref CollectionsMarshal.GetValueRefOrNullRef((Dictionary<HeaderDescriptor, object>)_headerStore
 420
 5802421                    info = ReplaceWithHeaderStoreItemInfo(ref storeValueRef, entry.Value);
 5802422                }
 423
 424                // Make sure we parse all raw values before returning the result. Note that this has to be
 425                // done before we calculate the array length (next line): A raw value may contain a list of
 426                // values.
 11390427                ParseRawHeaderValues(entry.Key, info);
 428
 11390429                string[] values = GetStoreValuesAsStringArray(entry.Key, info);
 11390430                yield return new KeyValuePair<string, IEnumerable<string>>(entry.Key.Name, values);
 11390431            }
 15453432        }
 433
 434        #endregion
 435
 436        #region IEnumerable Members
 437
 0438        Collections.IEnumerator Collections.IEnumerable.GetEnumerator() => GetEnumerator();
 439
 440        #endregion
 441
 442        internal void AddParsedValue(HeaderDescriptor descriptor, object value)
 0443        {
 0444            Debug.Assert(value != null);
 0445            Debug.Assert(descriptor.Parser != null, "Can't add parsed value if there is no parser available.");
 446
 0447            HeaderStoreItemInfo info = GetOrCreateHeaderInfo(descriptor);
 448
 449            // If the current header has only one value, we can't add another value. The strongly typed property
 450            // must not call AddParsedValue(), but SetParsedValue(). E.g. for headers like 'Date', 'Host'.
 0451            Debug.Assert(descriptor.Parser.SupportsMultipleValues, $"Header '{descriptor.Name}' doesn't support multiple
 452
 0453            AddParsedValue(info, value);
 0454        }
 455
 456        internal void SetParsedValue(HeaderDescriptor descriptor, object value)
 0457        {
 0458            Debug.Assert(value != null);
 0459            Debug.Assert(descriptor.Parser != null, "Can't add parsed value if there is no parser available.");
 460
 461            // This method will first clear all values. This is used e.g. when setting the 'Date' or 'Host' header.
 462            // i.e. headers not supporting collections.
 0463            HeaderStoreItemInfo info = GetOrCreateHeaderInfo(descriptor);
 464
 0465            info.ParsedAndInvalidValues = null;
 0466            info.RawValue = null;
 467
 0468            AddParsedValue(info, value);
 0469        }
 470
 471        internal void SetOrRemoveParsedValue(HeaderDescriptor descriptor, object? value)
 0472        {
 0473            if (value == null)
 0474            {
 0475                Remove(descriptor);
 0476            }
 477            else
 0478            {
 0479                SetParsedValue(descriptor, value);
 0480            }
 0481        }
 482
 483        public bool Remove(string name) =>
 0484            TryGetHeaderDescriptor(name, out HeaderDescriptor descriptor) &&
 0485            Remove(descriptor);
 486
 487        internal bool RemoveParsedValue(HeaderDescriptor descriptor, object value, bool removeAll = false)
 0488        {
 0489            Debug.Assert(value != null);
 490
 491            // If we have a value for this header, then verify if we have a single value. If so, compare that
 492            // value with 'item'. If we have a list of values, then remove 'item' from the list.
 0493            if (TryGetAndParseHeaderInfo(descriptor, out HeaderStoreItemInfo? info))
 0494            {
 0495                Debug.Assert(descriptor.Parser != null, "Can't add parsed value if there is no parser available.");
 0496                Debug.Assert(descriptor.Parser.SupportsMultipleValues,
 0497                    "This method should not be used for single-value headers. Use Remove(string) instead.");
 498
 499                // If there is no entry, just return.
 0500                var parsedValue = info.ParsedAndInvalidValues;
 0501                if (parsedValue == null)
 0502                {
 0503                    return false;
 504                }
 505
 0506                bool result = false;
 0507                IEqualityComparer? comparer = descriptor.Parser.Comparer;
 508
 0509                List<object>? parsedValues = parsedValue as List<object>;
 0510                if (parsedValues == null)
 0511                {
 0512                    if (parsedValue is not InvalidValue)
 0513                    {
 0514                        Debug.Assert(parsedValue.GetType() == value.GetType(),
 0515                            "Stored value does not have the same type as 'value'.");
 516
 0517                        if (AreEqual(value, parsedValue, comparer))
 0518                        {
 0519                            info.ParsedAndInvalidValues = null;
 0520                            result = true;
 0521                        }
 0522                    }
 0523                }
 524                else
 0525                {
 0526                    for (int i = 0; i < parsedValues.Count; i++)
 0527                    {
 0528                        object item = parsedValues[i];
 0529                        if (item is not InvalidValue)
 0530                        {
 0531                            Debug.Assert(item.GetType() == value.GetType(),
 0532                                "One of the stored values does not have the same type as 'value'.");
 533
 0534                            if (AreEqual(value, item, comparer))
 0535                            {
 0536                                parsedValues.RemoveAt(i);
 0537                                i--;
 538
 0539                                if (!result)
 0540                                {
 0541                                    result = true;
 542
 0543                                    if (!removeAll)
 0544                                    {
 0545                                        break;
 546                                    }
 0547                                }
 548                                else
 0549                                {
 550                                    // We've removed a second item. Fallback to RemoveAll in case there are more to main
 551                                    // Create a copy of the locals to avoid the capture allocation in the common case.
 0552                                    object valueLocal = value;
 0553                                    IEqualityComparer? comparerLocal = comparer;
 0554                                    parsedValues.RemoveAll(item => item is not InvalidValue && AreEqual(valueLocal, item
 0555                                    break;
 556                                }
 0557                            }
 0558                        }
 0559                    }
 560
 561                    // If we removed the last item in a list, remove the list.
 0562                    if (parsedValues.Count == 0)
 0563                    {
 0564                        info.AssertContainsNoInvalidValues();
 0565                        info.ParsedAndInvalidValues = null;
 0566                    }
 0567                }
 568
 569                // If there is no value for the header left, remove the header.
 0570                if (info.IsEmpty)
 0571                {
 0572                    bool headerRemoved = Remove(descriptor);
 0573                    Debug.Assert(headerRemoved, $"Existing header '{descriptor.Name}' couldn't be removed.");
 0574                }
 575
 0576                return result;
 577            }
 578
 0579            return false;
 0580        }
 581
 582        internal bool ContainsParsedValue(HeaderDescriptor descriptor, object value)
 0583        {
 0584            Debug.Assert(value != null);
 585
 586            // If we have a value for this header, then verify if we have a single value. If so, compare that
 587            // value with 'item'. If we have a list of values, then compare each item in the list with 'item'.
 0588            if (TryGetAndParseHeaderInfo(descriptor, out HeaderStoreItemInfo? info))
 0589            {
 0590                Debug.Assert(descriptor.Parser != null, "Can't add parsed value if there is no parser available.");
 0591                Debug.Assert(descriptor.Parser.SupportsMultipleValues,
 0592                    "This method should not be used for single-value headers. Use equality comparer instead.");
 593
 594                // If there is no entry, just return.
 0595                var parsedValue = info.ParsedAndInvalidValues;
 0596                if (parsedValue == null)
 0597                {
 0598                    return false;
 599                }
 600
 0601                List<object>? parsedValues = parsedValue as List<object>;
 602
 0603                IEqualityComparer? comparer = descriptor.Parser.Comparer;
 604
 0605                if (parsedValues == null)
 0606                {
 0607                    if (parsedValue is not InvalidValue)
 0608                    {
 0609                        Debug.Assert(parsedValue.GetType() == value.GetType(),
 0610                            "Stored value does not have the same type as 'value'.");
 611
 0612                        return AreEqual(value, parsedValue, comparer);
 613                    }
 0614                }
 615                else
 0616                {
 0617                    foreach (object item in parsedValues)
 0618                    {
 0619                        if (item is not InvalidValue)
 0620                        {
 0621                            Debug.Assert(item.GetType() == value.GetType(),
 0622                                "One of the stored values does not have the same type as 'value'.");
 623
 0624                            if (AreEqual(value, item, comparer))
 0625                            {
 0626                                return true;
 627                            }
 0628                        }
 0629                    }
 630
 0631                    return false;
 632                }
 0633            }
 634
 0635            return false;
 0636        }
 637
 638        internal virtual void AddHeaders(HttpHeaders sourceHeaders)
 0639        {
 0640            Debug.Assert(sourceHeaders != null);
 0641            Debug.Assert(GetType() == sourceHeaders.GetType(), "Can only copy headers from an instance of the same type.
 642
 643            // Only add header values if they're not already set on the message. Note that we don't merge
 644            // collections: If both the default headers and the message have set some values for a certain
 645            // header, then we don't try to merge the values.
 0646            if (_count == 0 && sourceHeaders._headerStore is HeaderEntry[] sourceEntries)
 0647            {
 648                // If the target collection is empty, we don't have to search for existing values
 0649                _count = sourceHeaders._count;
 0650                if (_headerStore is not HeaderEntry[] entries || entries.Length < _count)
 0651                {
 0652                    entries = new HeaderEntry[sourceEntries.Length];
 0653                    _headerStore = entries;
 0654                }
 655
 0656                for (int i = 0; i < _count && i < sourceEntries.Length; i++)
 0657                {
 0658                    HeaderEntry entry = sourceEntries[i];
 0659                    if (entry.Value is HeaderStoreItemInfo info)
 0660                    {
 0661                        entry.Value = CloneHeaderInfo(entry.Key, info);
 0662                    }
 0663                    entries[i] = entry;
 0664                }
 0665            }
 666            else
 0667            {
 0668                foreach (HeaderEntry entry in sourceHeaders.GetEntries())
 0669                {
 0670                    ref object? storeValueRef = ref GetValueRefOrAddDefault(entry.Key);
 0671                    if (storeValueRef is null)
 0672                    {
 0673                        object sourceValue = entry.Value;
 0674                        if (sourceValue is HeaderStoreItemInfo info)
 0675                        {
 0676                            storeValueRef = CloneHeaderInfo(entry.Key, info);
 0677                        }
 678                        else
 0679                        {
 0680                            Debug.Assert(sourceValue is string);
 0681                            storeValueRef = sourceValue;
 0682                        }
 0683                    }
 0684                }
 0685            }
 0686        }
 687
 688        private static HeaderStoreItemInfo CloneHeaderInfo(HeaderDescriptor descriptor, HeaderStoreItemInfo sourceInfo)
 0689        {
 0690            lock (sourceInfo)
 0691            {
 0692                var destinationInfo = new HeaderStoreItemInfo
 0693                {
 0694                    // Always copy raw values
 0695                    RawValue = CloneStringHeaderInfoValues(sourceInfo.RawValue)
 0696                };
 697
 0698                if (descriptor.Parser == null)
 0699                {
 0700                    sourceInfo.AssertContainsNoInvalidValues();
 0701                    destinationInfo.ParsedAndInvalidValues = CloneStringHeaderInfoValues(sourceInfo.ParsedAndInvalidValu
 0702                }
 703                else
 0704                {
 705                    // We have a parser, so we also have to clone invalid values and parsed values.
 0706                    if (sourceInfo.ParsedAndInvalidValues != null)
 0707                    {
 0708                        List<object>? sourceValues = sourceInfo.ParsedAndInvalidValues as List<object>;
 0709                        if (sourceValues == null)
 0710                        {
 0711                            CloneAndAddValue(destinationInfo, sourceInfo.ParsedAndInvalidValues);
 0712                        }
 713                        else
 0714                        {
 0715                            foreach (object item in sourceValues)
 0716                            {
 0717                                CloneAndAddValue(destinationInfo, item);
 0718                            }
 0719                        }
 0720                    }
 0721                }
 722
 0723                return destinationInfo;
 724            }
 0725        }
 726
 727        private static void CloneAndAddValue(HeaderStoreItemInfo destinationInfo, object source)
 0728        {
 729            // We only have one value. Clone it and assign it to the store.
 0730            if (source is ICloneable cloneableValue)
 0731            {
 0732                Debug.Assert(source is not InvalidValue);
 0733                AddParsedValue(destinationInfo, cloneableValue.Clone());
 0734            }
 735            else
 0736            {
 737                // If it doesn't implement ICloneable, it's a value type or an immutable type like String/Uri.
 0738                AddParsedValue(destinationInfo, source);
 0739            }
 0740        }
 741
 742        [return: NotNullIfNotNull(nameof(source))]
 743        private static object? CloneStringHeaderInfoValues(object? source)
 0744        {
 0745            if (source == null)
 0746            {
 0747                return null;
 748            }
 749
 0750            List<object>? sourceValues = source as List<object>;
 0751            if (sourceValues == null)
 0752            {
 753                // If we just have one value, return the reference to the string (strings are immutable so it's OK
 754                // to use the reference).
 0755                return source;
 756            }
 757            else
 0758            {
 759                // If we have a list of strings, create a new list and copy all strings to the new list.
 0760                return new List<object>(sourceValues);
 761            }
 0762        }
 763
 764        private HeaderStoreItemInfo GetOrCreateHeaderInfo(HeaderDescriptor descriptor)
 0765        {
 0766            if (TryGetAndParseHeaderInfo(descriptor, out HeaderStoreItemInfo? info))
 0767            {
 0768                return info;
 769            }
 770            else
 0771            {
 0772                return CreateAndAddHeaderToStore(descriptor);
 773            }
 0774        }
 775
 776        private HeaderStoreItemInfo CreateAndAddHeaderToStore(HeaderDescriptor descriptor)
 0777        {
 0778            Debug.Assert(!Contains(descriptor));
 779
 780            // If we don't have the header in the store yet, add it now.
 0781            HeaderStoreItemInfo result = new HeaderStoreItemInfo();
 782
 783            // If the descriptor header type is in _treatAsCustomHeaderTypes, it must be converted to a custom header be
 0784            Debug.Assert((descriptor.HeaderType & _treatAsCustomHeaderTypes) == 0);
 785
 0786            AddEntryToStore(new HeaderEntry(descriptor, result));
 787
 0788            return result;
 0789        }
 790
 791        internal bool TryGetHeaderValue(HeaderDescriptor descriptor, [NotNullWhen(true)] out object? value)
 0792        {
 0793            ref object storeValueRef = ref GetValueRefOrNullRef(descriptor);
 0794            if (Unsafe.IsNullRef(ref storeValueRef))
 0795            {
 0796                value = null;
 0797                return false;
 798            }
 799            else
 0800            {
 0801                value = storeValueRef;
 0802                return true;
 803            }
 0804        }
 805
 806        private bool TryGetAndParseHeaderInfo(HeaderDescriptor key, [NotNullWhen(true)] out HeaderStoreItemInfo? info)
 8338807        {
 8338808            ref object storeValueRef = ref GetValueRefOrNullRef(key);
 8338809            if (!Unsafe.IsNullRef(ref storeValueRef))
 2423810            {
 2423811                object value = storeValueRef;
 812
 2423813                info = value is HeaderStoreItemInfo hsi
 2423814                    ? hsi
 2423815                    : ReplaceWithHeaderStoreItemInfo(ref storeValueRef, value);
 816
 2423817                ParseRawHeaderValues(key, info);
 2423818                return true;
 819            }
 820
 5915821            info = null;
 5915822            return false;
 8338823        }
 824
 825        /// <summary>
 826        /// Replaces <paramref name="storeValueRef"/> with a new <see cref="HeaderStoreItemInfo"/>,
 827        /// or returns the existing <see cref="HeaderStoreItemInfo"/> if a different thread beat us to it.
 828        /// </summary>
 829        /// <remarks>
 830        /// This helper should be used any time we're upgrading a storage slot from an unparsed string to a HeaderStoreI
 831        /// Concurrent writes to the header collection are UB, so we don't need to worry about race conditions when doin
 832        /// </remarks>
 833        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 834        private static HeaderStoreItemInfo ReplaceWithHeaderStoreItemInfo(ref object storeValueRef, object value)
 5802835        {
 5802836            Debug.Assert(value is string);
 837
 5802838            var info = new HeaderStoreItemInfo() { RawValue = value };
 5802839            object previousValue = Interlocked.CompareExchange(ref storeValueRef, info, value);
 840
 5802841            if (ReferenceEquals(previousValue, value))
 5802842            {
 5802843                return info;
 844            }
 845
 846            // Rare race condition: Another thread replaced the value with a HeaderStoreItemInfo.
 0847            return (HeaderStoreItemInfo)previousValue;
 5802848        }
 849
 850        private static void ParseRawHeaderValues(HeaderDescriptor descriptor, HeaderStoreItemInfo info)
 13813851        {
 852            // Unlike TryGetHeaderInfo() this method tries to parse all non-validated header values (if any)
 853            // before returning to the caller.
 13813854            lock (info)
 13813855            {
 13813856                Debug.Assert(!info.IsEmpty);
 13813857                if (info.RawValue != null)
 8967858                {
 8967859                    if (info.RawValue is List<string> rawValues)
 3165860                    {
 22155861                        foreach (string rawValue in rawValues)
 6330862                        {
 6330863                            ParseSingleRawHeaderValue(info, descriptor, rawValue);
 6330864                        }
 3165865                    }
 866                    else
 5802867                    {
 5802868                        string? rawValue = info.RawValue as string;
 5802869                        Debug.Assert(rawValue is not null);
 5802870                        ParseSingleRawHeaderValue(info, descriptor, rawValue);
 5802871                    }
 872
 873                    // At this point all values are either in info.ParsedValue, info.InvalidValue. Reset RawValue.
 8967874                    Debug.Assert(info.ParsedAndInvalidValues is not null);
 8967875                    info.RawValue = null;
 8967876                }
 13813877            }
 13813878        }
 879
 880        private static void ParseSingleRawHeaderValue(HeaderStoreItemInfo info, HeaderDescriptor descriptor, string rawV
 12132881        {
 12132882            Debug.Assert(Monitor.IsEntered(info));
 12132883            if (descriptor.Parser == null)
 6330884            {
 6330885                if (HttpRuleParser.ContainsNewLineOrNull(rawValue))
 0886                {
 0887                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, SR.Format(SR.net_http_log_headers_no_
 0888                    AddInvalidValue(info, rawValue);
 0889                }
 890                else
 6330891                {
 6330892                    AddParsedValue(info, rawValue);
 6330893                }
 6330894            }
 895            else
 5802896            {
 5802897                if (!TryParseAndAddRawHeaderValue(descriptor, info, rawValue, true))
 3266898                {
 3266899                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Log.HeadersInvalidValue(descriptor.Name, rawValue
 3266900                }
 5802901            }
 12132902        }
 903
 904        // See Add(name, string)
 905        internal bool TryParseAndAddValue(HeaderDescriptor descriptor, string? value)
 0906        {
 907            // We don't use GetOrCreateHeaderInfo() here, since this would create a new header in the store. If parsing
 908            // the value then throws, we would have to remove the header from the store again. So just get a
 909            // HeaderStoreItemInfo object and try to parse the value. If it works, we'll add the header.
 910            HeaderStoreItemInfo info;
 911            bool addToStore;
 0912            PrepareHeaderInfoForAdd(descriptor, out info, out addToStore);
 913
 0914            bool result = TryParseAndAddRawHeaderValue(descriptor, info, value, false);
 915
 0916            if (result && addToStore && (info.ParsedAndInvalidValues != null))
 0917            {
 0918                info.AssertContainsNoInvalidValues();
 919                // If we get here, then the value could be parsed correctly. If we created a new HeaderStoreItemInfo, ad
 920                // it to the store if we added at least one value.
 0921                Debug.Assert(!Contains(descriptor));
 0922                AddEntryToStore(new HeaderEntry(descriptor, info));
 0923            }
 924
 0925            return result;
 0926        }
 927
 928        // See ParseAndAddValue
 929        private static bool TryParseAndAddRawHeaderValue(HeaderDescriptor descriptor, HeaderStoreItemInfo info, string? 
 5802930        {
 5802931            Debug.Assert(info != null);
 5802932            Debug.Assert(descriptor.Parser != null);
 933
 934            // Values are added as 'invalid' if we either can't parse the value OR if we already have a value
 935            // and the current header doesn't support multiple values: e.g. trying to add a date/time value
 936            // to the 'Date' header if we already have a date/time value will result in the second value being
 937            // added to the 'invalid' header values.
 5802938            if (!info.CanAddParsedValue(descriptor.Parser))
 0939            {
 0940                if (addWhenInvalid)
 0941                {
 0942                    AddInvalidValue(info, value ?? string.Empty);
 0943                }
 0944                return false;
 945            }
 946
 5802947            int index = 0;
 948
 5802949            if (descriptor.Parser.TryParseValue(value, info.ParsedAndInvalidValues, ref index, out object? parsedValue))
 3655950            {
 951                // The raw string only represented one value (which was successfully parsed). Add the value and return.
 3655952                if ((value == null) || (index == value.Length))
 1168953                {
 1168954                    if (parsedValue != null)
 1055955                    {
 1055956                        AddParsedValue(info, parsedValue);
 1055957                    }
 113958                    else if (addWhenInvalid && info.ParsedAndInvalidValues is null)
 113959                    {
 113960                        AddInvalidValue(info, value ?? string.Empty);
 113961                    }
 1168962                    return true;
 963                }
 2487964                Debug.Assert(index < value.Length, "Parser must return an index value within the string length.");
 965
 966                // If we successfully parsed a value, but there are more left to read, store the results in a temp
 967                // list. Only when all values are parsed successfully write the list to the store.
 2487968                List<object> parsedValues = new List<object>();
 2487969                if (parsedValue != null)
 2487970                {
 2487971                    parsedValues.Add(parsedValue);
 2487972                }
 973
 38619974                while (index < value.Length)
 37251975                {
 37251976                    if (descriptor.Parser.TryParseValue(value, info.ParsedAndInvalidValues, ref index, out parsedValue))
 36132977                    {
 36132978                        if (parsedValue != null)
 36132979                        {
 36132980                            parsedValues.Add(parsedValue);
 36132981                        }
 36132982                    }
 983                    else
 1119984                    {
 1119985                        if (addWhenInvalid)
 1119986                        {
 1119987                            AddInvalidValue(info, value);
 1119988                        }
 1119989                        return false;
 990                    }
 36132991                }
 992
 993                // All values were parsed correctly. Copy results to the store.
 62084994                foreach (object item in parsedValues)
 28990995                {
 28990996                    AddParsedValue(info, item);
 28990997                }
 998
 1368999                if (parsedValues.Count == 0 && addWhenInvalid && info.ParsedAndInvalidValues is null)
 01000                {
 01001                    AddInvalidValue(info, value);
 01002                }
 1003
 13681004                return true;
 1005            }
 1006
 21471007            Debug.Assert(value != null);
 21471008            if (addWhenInvalid)
 21471009            {
 21471010                AddInvalidValue(info, value ?? string.Empty);
 21471011            }
 21471012            return false;
 58021013        }
 1014
 1015        private static void AddParsedValue(HeaderStoreItemInfo info, object value)
 960271016        {
 960271017            Debug.Assert(!(value is List<object>),
 960271018                "Header value types must not derive from List<object> since this type is used internally to store " +
 960271019                "lists of values. So we would not be able to distinguish between a single value and a list of values.");
 1020
 960271021            AddValueToStoreValue<object>(value, ref info.ParsedAndInvalidValues);
 960271022        }
 1023
 1024        private static void AddInvalidValue(HeaderStoreItemInfo info, string value)
 33791025        {
 33791026            AddValueToStoreValue<object>(new InvalidValue(value), ref info.ParsedAndInvalidValues);
 33791027        }
 1028
 1029        private static void AddRawValue(HeaderStoreItemInfo info, string value)
 31651030        {
 31651031            AddValueToStoreValue<string>(value, ref info.RawValue);
 31651032        }
 1033
 1034        private static void AddValueToStoreValue<T>(T value, ref object? currentStoreValue) where T : class
 1025711035        {
 1036            // If there is no value set yet, then add current item as value (we don't create a list
 1037            // if not required). If 'info.Value' is already assigned then make sure 'info.Value' is a
 1038            // List<T> and append 'item' to the list.
 1025711039            if (currentStoreValue == null)
 113901040            {
 113901041                currentStoreValue = value;
 113901042            }
 1043            else
 911811044            {
 911811045                List<T>? storeValues = currentStoreValue as List<T>;
 1046
 911811047                if (storeValues == null)
 96831048                {
 96831049                    storeValues = new List<T>(2);
 96831050                    Debug.Assert(currentStoreValue is T);
 96831051                    storeValues.Add((T)currentStoreValue);
 96831052                    currentStoreValue = storeValues;
 96831053                }
 911811054                Debug.Assert(value is T);
 911811055                storeValues.Add((T)value);
 911811056            }
 1025711057        }
 1058
 1059        internal object? GetSingleParsedValue(HeaderDescriptor descriptor)
 01060        {
 01061            if (!TryGetAndParseHeaderInfo(descriptor, out HeaderStoreItemInfo? info))
 01062            {
 01063                return null;
 1064            }
 1065
 01066            return info.GetSingleParsedValue();
 01067        }
 1068
 1069        internal object? GetParsedAndInvalidValues(HeaderDescriptor descriptor)
 01070        {
 01071            if (!TryGetAndParseHeaderInfo(descriptor, out HeaderStoreItemInfo? info))
 01072            {
 01073                return null;
 1074            }
 1075
 01076            return info.ParsedAndInvalidValues;
 01077        }
 1078
 79181079        internal virtual bool IsAllowedHeaderName(HeaderDescriptor descriptor) => true;
 1080
 1081        private void CheckIsAllowedHeaderName(HeaderDescriptor descriptor)
 155661082        {
 155661083            if (!IsAllowedHeaderName(descriptor))
 01084            {
 01085                throw new InvalidOperationException(SR.Format(SR.net_http_headers_not_allowed_header_name, descriptor.Na
 1086            }
 155661087        }
 1088
 1089        private void PrepareHeaderInfoForAdd(HeaderDescriptor descriptor, out HeaderStoreItemInfo info, out bool addToSt
 83381090        {
 83381091            CheckIsAllowedHeaderName(descriptor);
 1092
 83381093            addToStore = false;
 83381094            if (!TryGetAndParseHeaderInfo(descriptor, out info!))
 59151095            {
 59151096                info = new HeaderStoreItemInfo();
 59151097                addToStore = true;
 59151098            }
 83381099        }
 1100
 1101        private static void ParseAndAddValue(HeaderDescriptor descriptor, HeaderStoreItemInfo info, string? value)
 83381102        {
 83381103            Debug.Assert(info != null);
 83381104            Debug.Assert(descriptor.Parser != null);
 1105
 1106            // If the header only supports 1 value, we can add the current value only if there is no
 1107            // value already set.
 83381108            if (!info.CanAddParsedValue(descriptor.Parser))
 741109            {
 741110                throw new FormatException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_heade
 1111            }
 1112
 82641113            int index = 0;
 82641114            object parsedValue = descriptor.Parser.ParseValue(value, info.ParsedAndInvalidValues, ref index);
 1115
 1116            // The raw string only represented one value (which was successfully parsed). Add the value and return.
 1117            // If value is null we still have to first call ParseValue() to allow the parser to decide whether null is
 1118            // a valid value. If it is (i.e. no exception thrown), we set the parsed value (if any) and return.
 61171119            if ((value == null) || (index == value.Length))
 22621120            {
 1121                // If the returned value is null, then it means the header accepts empty values. i.e. we don't throw
 1122                // but we don't add 'null' to the store either.
 22621123                if (parsedValue != null)
 16721124                {
 16721125                    AddParsedValue(info, parsedValue);
 16721126                }
 22621127                return;
 1128            }
 38551129            Debug.Assert(index < value.Length, "Parser must return an index value within the string length.");
 1130
 1131            // If we successfully parsed a value, but there are more left to read, store the results in a temp
 1132            // list. Only when all values are parsed successfully write the list to the store.
 38551133            List<object> parsedValues = new List<object>();
 38551134            if (parsedValue != null)
 38551135            {
 38551136                parsedValues.Add(parsedValue);
 38551137            }
 1138
 676091139            while (index < value.Length)
 648731140            {
 648731141                parsedValue = descriptor.Parser.ParseValue(value, info.ParsedAndInvalidValues, ref index);
 637541142                if (parsedValue != null)
 637541143                {
 637541144                    parsedValues.Add(parsedValue);
 637541145                }
 637541146            }
 1147
 1148            // All values were parsed correctly. Copy results to the store.
 1241681149            foreach (object item in parsedValues)
 579801150            {
 579801151                AddParsedValue(info, item);
 579801152            }
 49981153        }
 1154
 1155        internal HeaderDescriptor GetHeaderDescriptor(string name)
 155661156        {
 155661157            ArgumentException.ThrowIfNullOrEmpty(name);
 1158
 155661159            if (!HeaderDescriptor.TryGet(name, out HeaderDescriptor descriptor))
 01160            {
 01161                throw new FormatException(SR.Format(SR.net_http_headers_invalid_header_name, name));
 1162            }
 1163
 155661164            if ((descriptor.HeaderType & _allowedHeaderTypes) != 0)
 93751165            {
 93751166                return descriptor;
 1167            }
 61911168            else if ((descriptor.HeaderType & _treatAsCustomHeaderTypes) != 0)
 61911169            {
 61911170                return descriptor.AsCustomHeader();
 1171            }
 1172
 01173            throw new InvalidOperationException(SR.Format(SR.net_http_headers_not_allowed_header_name, name));
 155661174        }
 1175
 1176        internal bool TryGetHeaderDescriptor(string name, out HeaderDescriptor descriptor)
 146881177        {
 146881178            if (string.IsNullOrEmpty(name))
 01179            {
 01180                descriptor = default;
 01181                return false;
 1182            }
 1183
 146881184            if (HeaderDescriptor.TryGet(name, out descriptor))
 146881185            {
 146881186                HttpHeaderType headerType = descriptor.HeaderType;
 1187
 146881188                if ((headerType & _allowedHeaderTypes) != 0)
 64681189                {
 64681190                    return true;
 1191                }
 1192
 82201193                if ((headerType & _treatAsCustomHeaderTypes) != 0)
 33971194                {
 33971195                    descriptor = descriptor.AsCustomHeader();
 33971196                    return true;
 1197                }
 48231198            }
 1199
 48231200            return false;
 146881201        }
 1202
 1203        internal static void CheckContainsNewLineOrNull(string? value)
 667271204        {
 667271205            if (value == null)
 487751206            {
 487751207                return;
 1208            }
 1209
 179521210            if (HttpRuleParser.ContainsNewLineOrNull(value))
 8981211            {
 8981212                throw new FormatException(SR.net_http_headers_no_newlines_no_nul);
 1213            }
 658291214        }
 1215
 1216        internal static string[] GetStoreValuesAsStringArray(HeaderDescriptor descriptor, HeaderStoreItemInfo info)
 113901217        {
 113901218            GetStoreValuesAsStringOrStringArray(descriptor, info, out string? singleValue, out string[]? multiValue);
 113901219            Debug.Assert(singleValue is not null ^ multiValue is not null);
 113901220            return multiValue ?? new[] { singleValue! };
 113901221        }
 1222
 1223        internal static void GetStoreValuesAsStringOrStringArray(HeaderDescriptor descriptor, object sourceValues, out s
 169781224        {
 169781225            HeaderStoreItemInfo? info = sourceValues as HeaderStoreItemInfo;
 169781226            if (info is null)
 01227            {
 01228                Debug.Assert(sourceValues is string);
 01229                singleValue = (string)sourceValues;
 01230                multiValue = null;
 01231                return;
 1232            }
 1233
 169781234            lock (info)
 169781235            {
 169781236                int length = GetValueCount(info);
 1237
 1238                scoped Span<string?> values;
 169781239                singleValue = null;
 169781240                if (length == 1)
 53101241                {
 53101242                    multiValue = null;
 53101243                    values = new Span<string?>(ref singleValue);
 53101244                }
 1245                else
 116681246                {
 116681247                    Debug.Assert(length > 1, "The header should have been removed when it became empty");
 116681248                    values = (multiValue = new string[length])!;
 116681249                }
 1250
 169781251                int currentIndex = 0;
 169781252                ReadStoreValues<object?>(values, info.ParsedAndInvalidValues, descriptor.Parser, ref currentIndex);
 169781253                ReadStoreValues<string?>(values, info.RawValue, null, ref currentIndex);
 1254
 169781255                Debug.Assert(currentIndex == length);
 169781256            }
 169781257        }
 1258
 1259        internal static int GetStoreValuesIntoStringArray(HeaderDescriptor descriptor, object sourceValues, [NotNull] re
 01260        {
 01261            values ??= Array.Empty<string>();
 1262
 01263            HeaderStoreItemInfo? info = sourceValues as HeaderStoreItemInfo;
 01264            if (info is null)
 01265            {
 01266                Debug.Assert(sourceValues is string);
 1267
 01268                if (values.Length == 0)
 01269                {
 01270                    values = new string[1];
 01271                }
 1272
 01273                values[0] = (string)sourceValues;
 01274                return 1;
 1275            }
 1276
 01277            lock (info)
 01278            {
 01279                int length = GetValueCount(info);
 01280                Debug.Assert(length > 0);
 1281
 01282                if (values.Length < length)
 01283                {
 01284                    values = new string[length];
 01285                }
 1286
 01287                int currentIndex = 0;
 01288                ReadStoreValues<object?>(values!, info.ParsedAndInvalidValues, descriptor.Parser, ref currentIndex);
 01289                ReadStoreValues<string?>(values!, info.RawValue, null, ref currentIndex);
 01290                Debug.Assert(currentIndex == length);
 1291
 01292                return length;
 1293            }
 01294        }
 1295
 1296        private static int GetValueCount(HeaderStoreItemInfo info)
 169781297        {
 169781298            Debug.Assert(info != null);
 169781299            Debug.Assert(Monitor.IsEntered(info));
 1300
 169781301            return Count<object>(info.ParsedAndInvalidValues) + Count<string>(info.RawValue);
 1302
 1303            static int Count<T>(object? valueStore) =>
 339561304                valueStore is null ? 0 :
 339561305                valueStore is List<T> list ? list.Count :
 339561306                1;
 169781307        }
 1308
 1309        private static void ReadStoreValues<T>(Span<string?> values, object? storeValue, HttpHeaderParser? parser, ref i
 339561310        {
 339561311            if (storeValue != null)
 169781312            {
 169781313                List<T>? storeValues = storeValue as List<T>;
 1314
 169781315                if (storeValues == null)
 53101316                {
 53101317                    values[currentIndex] = parser == null || storeValue is InvalidValue ? storeValue.ToString() : parser
 53101318                    currentIndex++;
 53101319                }
 1320                else
 116681321                {
 3551601322                    foreach (object? item in storeValues)
 1600781323                    {
 1600781324                        Debug.Assert(item != null);
 1600781325                        values[currentIndex] = parser == null || item is InvalidValue ? item.ToString() : parser.ToStrin
 1600781326                        currentIndex++;
 1600781327                    }
 116681328                }
 169781329            }
 339561330        }
 1331
 1332        private static bool AreEqual(object value, object? storeValue, IEqualityComparer? comparer)
 01333        {
 01334            Debug.Assert(value != null);
 1335
 01336            if (comparer != null)
 01337            {
 01338                return comparer.Equals(value, storeValue);
 1339            }
 1340
 1341            // We don't have a comparer, so use the Equals() method.
 01342            return value.Equals(storeValue);
 01343        }
 1344
 1345        internal sealed class InvalidValue
 1346        {
 1347            private readonly string _value;
 1348
 33791349            public InvalidValue(string value)
 33791350            {
 33791351                Debug.Assert(value is not null);
 33791352                _value = value;
 33791353            }
 1354
 33791355            public override string ToString() => _value;
 1356        }
 1357
 1358        internal sealed class HeaderStoreItemInfo
 1359        {
 446461360            internal HeaderStoreItemInfo() { }
 1361
 1362            internal object? RawValue;
 1363            internal object? ParsedAndInvalidValues;
 1364
 1365            public bool CanAddParsedValue(HttpHeaderParser parser)
 141401366            {
 141401367                Debug.Assert(parser != null, "There should be no reason to call CanAddValue if there is no parser for th
 1368
 1369                // If the header only supports one value, and we have already a value set, then we can't add
 1370                // another value. E.g. the 'Date' header only supports one value. We can't add multiple timestamps
 1371                // to 'Date'.
 1372                // So if this is a known header, ask the parser if it supports multiple values and check whether
 1373                // we already have a (valid or invalid) value.
 1374                // Note that we ignore the rawValue by purpose: E.g. we are parsing 2 raw values for a header only
 1375                // supporting 1 value. When the first value gets parsed, CanAddValue returns true and we add the
 1376                // parsed value to ParsedValue. When the second value is parsed, CanAddValue returns false, because
 1377                // we have already a parsed value.
 141401378                return parser.SupportsMultipleValues || ParsedAndInvalidValues is null;
 141401379            }
 1380
 1381            [Conditional("DEBUG")]
 1382            public void AssertContainsNoInvalidValues()
 24231383            {
 24231384                if (ParsedAndInvalidValues is not null)
 24231385                {
 24231386                    if (ParsedAndInvalidValues is List<object> list)
 13681387                    {
 620841388                        foreach (object item in list)
 289901389                        {
 289901390                            Debug.Assert(item is not InvalidValue);
 289901391                        }
 13681392                    }
 1393                    else
 10551394                    {
 10551395                        Debug.Assert(ParsedAndInvalidValues is not InvalidValue);
 10551396                    }
 24231397                }
 24231398            }
 1399
 1400            public object? GetSingleParsedValue()
 01401            {
 01402                if (ParsedAndInvalidValues is not null)
 01403                {
 01404                    if (ParsedAndInvalidValues is List<object> list)
 01405                    {
 01406                        AssertContainsSingleParsedValue(list);
 01407                        foreach (object item in list)
 01408                        {
 01409                            if (item is not InvalidValue)
 01410                            {
 01411                                return item;
 1412                            }
 01413                        }
 01414                    }
 1415                    else
 01416                    {
 01417                        if (ParsedAndInvalidValues is not InvalidValue)
 01418                        {
 01419                            return ParsedAndInvalidValues;
 1420                        }
 01421                    }
 01422                }
 1423
 01424                return null;
 01425            }
 1426
 1427            [Conditional("DEBUG")]
 1428            private static void AssertContainsSingleParsedValue(List<object> list)
 01429            {
 01430                int count = 0;
 01431                foreach (object item in list)
 01432                {
 01433                    if (item is not InvalidValue)
 01434                    {
 01435                        count++;
 01436                    }
 01437                }
 1438
 01439                Debug.Assert(count == 1, "Only a single parsed value should be stored for this parser");
 01440            }
 1441
 138131442            public bool IsEmpty => RawValue == null && ParsedAndInvalidValues == null;
 1443        }
 1444
 1445
 1446        #region Low-level implementation details that work with _headerStore directly
 1447
 1448        private const int InitialCapacity = 4;
 1449        internal const int ArrayThreshold = 64; // Above this threshold, header ordering will not be preserved
 1450
 1451        internal HeaderEntry[]? GetEntriesArray()
 253181452        {
 253181453            object? store = _headerStore;
 253181454            if (store is null)
 01455            {
 01456                return null;
 1457            }
 253181458            else if (store is HeaderEntry[] entries)
 253181459            {
 253181460                return entries;
 1461            }
 1462            else
 01463            {
 01464                return GetEntriesFromDictionary();
 1465            }
 1466
 1467            HeaderEntry[] GetEntriesFromDictionary()
 01468            {
 01469                var dictionary = (Dictionary<HeaderDescriptor, object>)_headerStore!;
 01470                var entries = new HeaderEntry[dictionary.Count];
 01471                int i = 0;
 01472                foreach (KeyValuePair<HeaderDescriptor, object> entry in dictionary)
 01473                {
 01474                    entries[i++] = new HeaderEntry
 01475                    {
 01476                        Key = entry.Key,
 01477                        Value = entry.Value
 01478                    };
 01479                }
 01480                return entries;
 01481            }
 253181482        }
 1483
 1484        internal ReadOnlySpan<HeaderEntry> GetEntries()
 01485        {
 01486            return new ReadOnlySpan<HeaderEntry>(GetEntriesArray(), 0, _count);
 01487        }
 1488
 98651489        internal int Count => _count;
 1490
 58021491        private bool EntriesAreLiveView => _headerStore is HeaderEntry[];
 1492
 1493        private ref object GetValueRefOrNullRef(HeaderDescriptor key)
 131841494        {
 131841495            ref object valueRef = ref Unsafe.NullRef<object>();
 1496
 131841497            object? store = _headerStore;
 131841498            if (store is HeaderEntry[] entries)
 131841499            {
 263681500                for (int i = 0; i < _count && i < entries.Length; i++)
 24231501                {
 24231502                    if (key.Equals(entries[i].Key))
 24231503                    {
 24231504                        valueRef = ref entries[i].Value;
 24231505                        break;
 1506                    }
 01507                }
 131841508            }
 01509            else if (store is not null)
 01510            {
 01511                valueRef = ref CollectionsMarshal.GetValueRefOrNullRef((Dictionary<HeaderDescriptor, object>)store, key)
 01512            }
 1513
 131841514            return ref valueRef;
 131841515        }
 1516
 1517        private ref object? GetValueRefOrAddDefault(HeaderDescriptor key)
 161951518        {
 161951519            object? store = _headerStore;
 161951520            if (store is HeaderEntry[] entries)
 161921521            {
 323841522                for (int i = 0; i < _count && i < entries.Length; i++)
 31651523                {
 31651524                    if (key.Equals(entries[i].Key))
 31651525                    {
 31651526                        return ref entries[i].Value!;
 1527                    }
 01528                }
 1529
 130271530                int count = _count;
 130271531                _count++;
 130271532                if ((uint)count < (uint)entries.Length)
 130271533                {
 130271534                    entries[count].Key = key;
 130271535                    return ref entries[count].Value!;
 1536                }
 1537
 01538                return ref GrowEntriesAndAddDefault(key);
 1539            }
 31540            else if (store is null)
 31541            {
 31542                _count++;
 31543                entries = new HeaderEntry[InitialCapacity];
 31544                _headerStore = entries;
 31545                entries[0].Key = key;
 31546                return ref entries[0].Value!;
 1547            }
 1548            else
 01549            {
 01550                return ref DictionaryGetValueRefOrAddDefault(key);
 1551            }
 1552
 1553            ref object? GrowEntriesAndAddDefault(HeaderDescriptor key)
 01554            {
 01555                var entries = (HeaderEntry[])_headerStore!;
 01556                if (entries.Length == ArrayThreshold)
 01557                {
 01558                    return ref ConvertToDictionaryAndAddDefault(key);
 1559                }
 1560                else
 01561                {
 01562                    Array.Resize(ref entries, entries.Length << 1);
 01563                    _headerStore = entries;
 01564                    ref HeaderEntry firstNewEntry = ref entries[entries.Length >> 1];
 01565                    firstNewEntry.Key = key;
 01566                    return ref firstNewEntry.Value!;
 1567                }
 01568            }
 1569
 1570            ref object? ConvertToDictionaryAndAddDefault(HeaderDescriptor key)
 01571            {
 01572                var entries = (HeaderEntry[])_headerStore!;
 01573                var dictionary = new Dictionary<HeaderDescriptor, object>(ArrayThreshold);
 01574                _headerStore = dictionary;
 01575                foreach (HeaderEntry entry in entries)
 01576                {
 01577                    dictionary.Add(entry.Key, entry.Value);
 01578                }
 01579                Debug.Assert(dictionary.Count == _count - 1);
 01580                return ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out _);
 01581            }
 1582
 1583            ref object? DictionaryGetValueRefOrAddDefault(HeaderDescriptor key)
 01584            {
 01585                var dictionary = (Dictionary<HeaderDescriptor, object>)_headerStore!;
 01586                ref object? value = ref CollectionsMarshal.GetValueRefOrAddDefault(dictionary, key, out _);
 01587                if (value is null)
 01588                {
 01589                    _count++;
 01590                }
 01591                return ref value;
 01592            }
 161951593        }
 1594
 1595        private void AddEntryToStore(HeaderEntry entry)
 24231596        {
 24231597            Debug.Assert(!Contains(entry.Key));
 1598
 24231599            if (_headerStore is HeaderEntry[] entries)
 24231600            {
 24231601                int count = _count;
 24231602                if ((uint)count < (uint)entries.Length)
 24231603                {
 24231604                    entries[count] = entry;
 24231605                    _count++;
 24231606                    return;
 1607                }
 01608            }
 1609
 01610            GetValueRefOrAddDefault(entry.Key) = entry.Value;
 24231611        }
 1612
 1613        internal bool Contains(HeaderDescriptor key)
 48461614        {
 48461615            return !Unsafe.IsNullRef(ref GetValueRefOrNullRef(key));
 48461616        }
 1617
 1618        public void Clear()
 245531619        {
 245531620            if (_headerStore is HeaderEntry[] entries)
 245381621            {
 245381622                Array.Clear(entries, 0, _count);
 245381623            }
 1624            else
 151625            {
 151626                _headerStore = null;
 151627            }
 245531628            _count = 0;
 245531629        }
 1630
 1631        internal bool Remove(HeaderDescriptor key)
 01632        {
 01633            bool removed = false;
 1634
 01635            object? store = _headerStore;
 01636            if (store is HeaderEntry[] entries)
 01637            {
 01638                for (int i = 0; i < _count && i < entries.Length; i++)
 01639                {
 01640                    if (key.Equals(entries[i].Key))
 01641                    {
 01642                        while (i + 1 < _count && (uint)(i + 1) < (uint)entries.Length)
 01643                        {
 01644                            entries[i] = entries[i + 1];
 01645                            i++;
 01646                        }
 01647                        entries[i] = default;
 01648                        removed = true;
 01649                        break;
 1650                    }
 01651                }
 01652            }
 01653            else if (store is not null)
 01654            {
 01655                removed = ((Dictionary<HeaderDescriptor, object>)store).Remove(key);
 01656            }
 1657
 01658            if (removed)
 01659            {
 01660                _count--;
 01661            }
 1662
 01663            return removed;
 01664        }
 1665
 1666        #endregion // _headerStore implementation
 1667    }
 1668}

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)