< Summary

Information
Class: System.Net.Http.Headers.HttpHeaderValueCollection<T>
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\Headers\HttpHeaderValueCollection.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 122
Coverable lines: 122
Total lines: 230
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 36
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
Add(...)100%110%
ParseAdd(...)100%110%
TryParseAdd(...)100%110%
Clear()100%110%
Contains(...)100%110%
CopyTo(...)0%14140%
Remove(...)100%110%
GetEnumerator()0%440%
Iterate()0%660%
System.Collections.IEnumerable.GetEnumerator()100%110%
ToString()100%110%
CheckValue(...)0%220%
GetCount()0%10100%

File(s)

D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\Headers\HttpHeaderValueCollection.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.Generic;
 5using System.Diagnostics;
 6
 7namespace System.Net.Http.Headers
 8{
 9    // This type is used for headers supporting a list of values. It essentially just forwards calls to
 10    // the actual header-store in HttpHeaders.
 11    //
 12    // This type can deal with a so called "special value": The RFC defines some headers which are collection of
 13    // values, but the RFC only defines 1 value, e.g. Transfer-Encoding: chunked, Connection: close,
 14    // Expect: 100-continue.
 15    // We expose strongly typed properties for these special values: TransferEncodingChunked, ConnectionClose,
 16    // ExpectContinue.
 17    // So we have 2 properties for each of these headers ('Transfer-Encoding' => TransferEncoding,
 18    // TransferEncodingChunked; 'Connection' => Connection, ConnectionClose; 'Expect' => Expect, ExpectContinue)
 19    //
 20    // The following solution was chosen:
 21    // - Keep HttpHeaders clean: HttpHeaders is unaware of these "special values"; it just stores the collection of
 22    //   headers.
 23    // - It is the responsibility of "higher level" components (HttpHeaderValueCollection, HttpRequestHeaders,
 24    //   HttpResponseHeaders) to deal with special values.
 25    // - HttpHeaderValueCollection can be configured with an IEqualityComparer and a "special value".
 26    //
 27    // Example: Server sends header "Transfer-Encoding: gzip, custom, chunked" to the client.
 28    // - HttpHeaders: HttpHeaders will have an entry in the header store for "Transfer-Encoding" with values
 29    //   "gzip", "custom", "chunked"
 30    // - HttpGeneralHeaders:
 31    //   - Property TransferEncoding: has three values "gzip", "custom", and "chunked"
 32    //   - Property TransferEncodingChunked: is set to "true".
 33    public sealed class HttpHeaderValueCollection<T> : ICollection<T> where T : class
 34    {
 35        private readonly HeaderDescriptor _descriptor;
 36        private readonly HttpHeaders _store;
 37
 38        public int Count
 39        {
 040            get { return GetCount(); }
 41        }
 42
 43        public bool IsReadOnly
 44        {
 045            get { return false; }
 46        }
 47
 048        internal HttpHeaderValueCollection(HeaderDescriptor descriptor, HttpHeaders store)
 049        {
 050            _store = store;
 051            _descriptor = descriptor;
 052        }
 53
 54        public void Add(T item)
 055        {
 056            CheckValue(item);
 057            _store.AddParsedValue(_descriptor, item);
 058        }
 59
 60        public void ParseAdd(string? input)
 061        {
 062            _store.Add(_descriptor, input);
 063        }
 64
 65        public bool TryParseAdd(string? input)
 066        {
 067            return _store.TryParseAndAddValue(_descriptor, input);
 068        }
 69
 70        public void Clear()
 071        {
 072            _store.Remove(_descriptor);
 073        }
 74
 75        public bool Contains(T item)
 076        {
 077            CheckValue(item);
 078            return _store.ContainsParsedValue(_descriptor, item);
 079        }
 80
 81        public void CopyTo(T[] array, int arrayIndex)
 082        {
 083            ArgumentNullException.ThrowIfNull(array);
 84
 85            // Allow arrayIndex == array.Length in case our own collection is empty
 086            ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex);
 087            ArgumentOutOfRangeException.ThrowIfGreaterThan(arrayIndex, array.Length);
 88
 089            object? storeValue = _store.GetParsedAndInvalidValues(_descriptor);
 90
 091            if (storeValue == null)
 092            {
 093                return;
 94            }
 95
 096            List<object>? storeValues = storeValue as List<object>;
 97
 098            if (storeValues == null)
 099            {
 0100                if (storeValue is not HttpHeaders.InvalidValue)
 0101                {
 0102                    Debug.Assert(storeValue is T);
 0103                    if (arrayIndex == array.Length)
 0104                    {
 0105                        throw new ArgumentException(SR.net_http_copyto_array_too_small);
 106                    }
 0107                    array[arrayIndex] = (T)storeValue;
 0108                }
 0109            }
 110            else
 0111            {
 0112                foreach (object item in storeValues)
 0113                {
 0114                    if (item is not HttpHeaders.InvalidValue)
 0115                    {
 0116                        Debug.Assert(item is T);
 0117                        if (arrayIndex == array.Length)
 0118                        {
 0119                            throw new ArgumentException(SR.net_http_copyto_array_too_small);
 120                        }
 0121                        array[arrayIndex++] = (T)item;
 0122                    }
 0123                }
 0124            }
 0125        }
 126
 127        public bool Remove(T item)
 0128        {
 0129            CheckValue(item);
 0130            return _store.RemoveParsedValue(_descriptor, item);
 0131        }
 132
 133        #region IEnumerable<T> Members
 134
 135        public IEnumerator<T> GetEnumerator()
 0136        {
 0137            object? storeValue = _store.GetParsedAndInvalidValues(_descriptor);
 0138            return storeValue is null || storeValue is HttpHeaders.InvalidValue ?
 0139                ((IEnumerable<T>)Array.Empty<T>()).GetEnumerator() : // use singleton empty array enumerator
 0140                Iterate(storeValue);
 141
 142            static IEnumerator<T> Iterate(object storeValue)
 0143            {
 0144                if (storeValue is List<object> storeValues)
 0145                {
 146                    // We have multiple values. Iterate through the values and return them.
 0147                    foreach (object item in storeValues)
 0148                    {
 0149                        if (item is HttpHeaders.InvalidValue)
 0150                        {
 0151                            continue;
 152                        }
 0153                        Debug.Assert(item is T);
 0154                        yield return (T)item;
 0155                    }
 0156                }
 157                else
 0158                {
 0159                    Debug.Assert(storeValue is T);
 0160                    yield return (T)storeValue;
 0161                }
 0162            }
 0163        }
 164
 165        #endregion
 166
 167        #region IEnumerable Members
 168
 169        Collections.IEnumerator Collections.IEnumerable.GetEnumerator()
 0170        {
 0171            return GetEnumerator();
 0172        }
 173
 174        #endregion
 175
 176        public override string ToString()
 0177        {
 0178            return _store.GetHeaderString(_descriptor);
 0179        }
 180
 181        private void CheckValue(T item)
 0182        {
 0183            ArgumentNullException.ThrowIfNull(item);
 184
 0185            if (_descriptor.Parser == GenericHeaderParser.TokenListParser)
 0186            {
 187                // The collection expects valid HTTP tokens, which are typed as string.
 188                // Unlike other parsed values (which are always valid by construction),
 189                // we can't assume the provided string is a valid token. So validate it before we use it.
 0190                Debug.Assert(typeof(T) == typeof(string));
 0191                HeaderUtilities.CheckValidToken((string)(object)item, nameof(item));
 0192            }
 0193        }
 194
 195        private int GetCount()
 0196        {
 197            // This is an O(n) operation.
 198
 0199            object? storeValue = _store.GetParsedAndInvalidValues(_descriptor);
 200
 0201            if (storeValue == null)
 0202            {
 0203                return 0;
 204            }
 205
 0206            List<object>? storeValues = storeValue as List<object>;
 207
 0208            if (storeValues == null)
 0209            {
 0210                if (storeValue is not HttpHeaders.InvalidValue)
 0211                {
 0212                    return 1;
 213                }
 0214                return 0;
 215            }
 216            else
 0217            {
 0218                int count = 0;
 0219                foreach (object item in storeValues)
 0220                {
 0221                    if (item is not HttpHeaders.InvalidValue)
 0222                    {
 0223                        count++;
 0224                    }
 0225                }
 0226                return count;
 227            }
 0228        }
 229    }
 230}