< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.IdentityModel.Tokens.MruSessionSecurityTokenCache
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/MruSessionSecurityTokenCache.cs
Line coverage
15%
Covered lines: 16
Uncovered lines: 89
Coverable lines: 105
Total lines: 337
Line coverage: 15.2%
Branch coverage
3%
Covered branches: 2
Total branches: 54
Branch coverage: 3.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%110%
.ctor(...)50%4483.33%
.ctor()100%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
.ctor(...)100%110%
Remove(...)0%660%
AddOrUpdate(...)0%220%
Get(...)0%14140%
RemoveAll(...)0%12120%
RemoveAll(...)100%110%
GetAll(...)0%12120%
Purge()0%440%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/IdentityModel/Tokens/MruSessionSecurityTokenCache.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;
 5using System.Collections.Generic;
 6using System.Collections.ObjectModel;
 7
 8namespace CoreWCF.IdentityModel.Tokens
 9{
 10    /// <summary>
 11    /// An MRU cache (Most Recently Used).
 12    /// </summary>
 13    /// <remarks>
 14    /// Thread safe. Critsec around each method.
 15    /// A LinkedList is used to track MRU for fast purge.
 16    /// A Dictionary is used for fast keyed lookup.
 17    /// Grows until it reaches this.maximumSize, then purges down to this.sizeAfterPurge.
 18    /// </remarks>
 19    internal class MruSessionSecurityTokenCache : SessionSecurityTokenCache
 20    {
 21        public const int DefaultTokenCacheSize = 20000;
 022        public static readonly TimeSpan DefaultPurgeInterval = TimeSpan.FromMinutes(15);
 23        private Dictionary<SessionSecurityTokenCacheKey, CacheEntry> _items;
 24        private CacheEntry _mruEntry;
 25        private LinkedList<SessionSecurityTokenCacheKey> _mruList;
 26        private int _sizeAfterPurge;
 1027        private object _syncRoot = new object();
 28
 29        /// <summary>
 30        /// Constructor to create an instance of this class.
 31        /// </summary>
 32        /// <remarks>
 33        /// Uses the default maximum cache size.
 34        /// </remarks>
 35        public MruSessionSecurityTokenCache()
 1036            : this(DefaultTokenCacheSize)
 37        {
 1038        }
 39
 40        /// <summary>
 41        /// Constructor to create an instance of this class.
 42        /// </summary>
 43        /// <param name="maximumSize">Defines the maximum size of the cache.</param>
 44        public MruSessionSecurityTokenCache(int maximumSize)
 1045            : this(maximumSize, null)
 46        {
 1047        }
 48
 49        /// <summary>
 50        /// Constructor to create an instance of this class.
 51        /// </summary>
 52        /// <param name="maximumSize">Defines the maximum size of the cache.</param>
 53        /// <param name="comparer">The method used for comparing cache entries.</param>
 54        public MruSessionSecurityTokenCache(int maximumSize, IEqualityComparer<SessionSecurityTokenCacheKey> comparer)
 1055            : this((maximumSize / 5) * 4, maximumSize, comparer)
 56        {
 1057        }
 58
 59        /// <summary>
 60        /// Constructor to create an instance of this class.
 61        /// </summary>
 62        /// <param name="sizeAfterPurge">
 63        /// If the cache size exceeds <paramref name="maximumSize"/>,
 64        /// the cache will be resized to <paramref name="sizeAfterPurge"/> by removing least recently used items.
 65        /// </param>
 66        /// <param name="maximumSize">Defines the maximum size of the cache.</param>
 67        public MruSessionSecurityTokenCache(int sizeAfterPurge, int maximumSize)
 068            : this(sizeAfterPurge, maximumSize, null)
 69        {
 070        }
 71
 72        /// <summary>
 73        /// Constructor to create an instance of this class.
 74        /// </summary>
 75        /// <param name="sizeAfterPurge">Specifies the size to which the cache is purged after it reaches <paramref name
 76        /// <param name="maximumSize">Specifies the maximum size of the cache.</param>
 77        /// <param name="comparer">Specifies the method used for comparing cache entries.</param>
 1078        public MruSessionSecurityTokenCache(int sizeAfterPurge, int maximumSize, IEqualityComparer<SessionSecurityTokenC
 79        {
 1080            if (sizeAfterPurge < 0)
 81            {
 082                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.ID0008), na
 83            }
 84
 1085            if (sizeAfterPurge >= maximumSize)
 86            {
 087                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.ID0009), na
 88            }
 89
 90            // null comparer is ok
 1091            _items = new Dictionary<SessionSecurityTokenCacheKey, CacheEntry>(maximumSize, comparer);
 1092            MaximumSize = maximumSize;
 1093            _mruList = new LinkedList<SessionSecurityTokenCacheKey>();
 1094            this._sizeAfterPurge = sizeAfterPurge;
 1095            _mruEntry = new CacheEntry();
 1096        }
 97
 98        /// <summary>
 99        /// Gets the maximum size of the cache
 100        /// </summary>
 0101        public int MaximumSize { get; }
 102
 103        /// <summary>
 104        /// Deletes the specified cache entry from the MruCache.
 105        /// </summary>
 106        /// <param name="key">Specifies the key for the entry to be deleted.</param>
 107        /// <exception cref="ArgumentNullException">The <paramref name="key"/> is null.</exception>
 108        public override void Remove(SessionSecurityTokenCacheKey key)
 109        {
 0110            if (key == null)
 111            {
 0112                return;
 113            }
 114
 0115            lock (_syncRoot)
 116            {
 117                CacheEntry entry;
 0118                if (_items.TryGetValue(key, out entry))
 119                {
 0120                    _items.Remove(key);
 0121                    _mruList.Remove(entry.Node);
 0122                    if (object.ReferenceEquals(_mruEntry.Node, entry.Node))
 123                    {
 0124                        _mruEntry.Value = null;
 0125                        _mruEntry.Node = null;
 126                    }
 127                }
 0128            }
 0129        }
 130
 131        /// <summary>
 132        /// Attempts to add an entry to the cache or update an existing one.
 133        /// </summary>
 134        /// <param name="key">The key for the entry to be added.</param>
 135        /// <param name="value">The security token to be added to the cache.</param>
 136        /// <param name="expirationTime">The expiration time for this entry.</param>
 137        public override void AddOrUpdate(SessionSecurityTokenCacheKey key, SessionSecurityToken value, DateTime expirati
 138        {
 0139            if (key == null)
 140            {
 0141                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("key");
 142            }
 143
 0144            lock (_syncRoot)
 145            {
 0146                Purge();
 0147                Remove(key);
 148
 149                // Add  the new entry to the cache and make it the MRU element
 0150                CacheEntry entry = new CacheEntry();
 0151                entry.Node = _mruList.AddFirst(key);
 0152                entry.Value = value;
 0153                _items.Add(key, entry);
 0154                _mruEntry = entry;
 0155            }
 0156        }
 157
 158        /// <summary>
 159        /// Returns the Session Security Token corresponding to the specified key exists in the cache. Also if it exists
 160        /// </summary>
 161        /// <param name="key">Specifies the key for the entry to be retrieved.</param>
 162        /// <returns>Returns the Session Security Token from the cache if found, otherwise, null.</returns>
 163        public override SessionSecurityToken Get(SessionSecurityTokenCacheKey key)
 164        {
 0165            if (key == null)
 166            {
 0167                return null;
 168            }
 169
 170            // If found, make the entry most recently used
 0171            SessionSecurityToken sessionToken = null;
 172            CacheEntry entry;
 173            bool found;
 174
 0175            lock (_syncRoot)
 176            {
 177                // first check our MRU item
 0178                if (_mruEntry.Node != null && key != null && key.Equals(_mruEntry.Node.Value))
 179                {
 0180                    return _mruEntry.Value;
 181                }
 182
 0183                found = _items.TryGetValue(key, out entry);
 0184                if (found)
 185                {
 0186                    sessionToken = entry.Value;
 187
 188                    // Move the node to the head of the MRU list if it's not already there
 0189                    if (_mruList.Count > 1 && !object.ReferenceEquals(_mruList.First, entry.Node))
 190                    {
 0191                        _mruList.Remove(entry.Node);
 0192                        _mruList.AddFirst(entry.Node);
 0193                        _mruEntry = entry;
 194                    }
 195                }
 0196            }
 197
 0198            return sessionToken;
 0199        }
 200
 201        /// <summary>
 202        /// Deletes matching cache entries from the MruCache.
 203        /// </summary>
 204        /// <param name="endpointId">Specifies the endpointId for the entries to be deleted.</param>
 205        /// <param name="contextId">Specifies the contextId for the entries to be deleted.</param>
 206        public override void RemoveAll(string endpointId, System.Xml.UniqueId contextId)
 207        {
 0208            if (null == contextId || string.IsNullOrEmpty(endpointId))
 209            {
 0210                return;
 211            }
 212
 0213            Dictionary<SessionSecurityTokenCacheKey, CacheEntry> entriesToDelete = new Dictionary<SessionSecurityTokenCa
 0214            SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(endpointId, contextId, null);
 0215            key.IgnoreKeyGeneration = true;
 0216            lock (_syncRoot)
 217            {
 0218                foreach (SessionSecurityTokenCacheKey itemKey in _items.Keys)
 219                {
 0220                    if (itemKey.Equals(key))
 221                    {
 0222                        entriesToDelete.Add(itemKey, _items[itemKey]);
 223                    }
 224                }
 225
 0226                foreach (SessionSecurityTokenCacheKey itemKey in entriesToDelete.Keys)
 227                {
 0228                    _items.Remove(itemKey);
 0229                    CacheEntry entry = entriesToDelete[itemKey];
 0230                    _mruList.Remove(entry.Node);
 0231                    if (object.ReferenceEquals(_mruEntry.Node, entry.Node))
 232                    {
 0233                        _mruEntry.Value = null;
 0234                        _mruEntry.Node = null;
 235                    }
 236                }
 237            }
 0238        }
 239
 240        /// <summary>
 241        /// Attempts to remove all entries with a matching endpoint Id from the cache.
 242        /// </summary>
 243        /// <param name="endpointId">The endpoint id for the entry to be removed.</param>
 244        public override void RemoveAll(string endpointId)
 245        {
 0246            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException(SR.Format(SR.ID4294)))
 247        }
 248
 249        /// <summary>
 250        /// Returns all the entries that match the given key.
 251        /// </summary>
 252        /// <param name="endpointId">The endpoint id for the entries to be retrieved.</param>
 253        /// <param name="contextId">The context id for the entries to be retrieved.</param>
 254        /// <returns>A collection of all the matching entries, an empty collection of no match found.</returns>
 255        public override IEnumerable<SessionSecurityToken> GetAll(string endpointId, System.Xml.UniqueId contextId)
 256        {
 0257            Collection<SessionSecurityToken> tokens = new Collection<SessionSecurityToken>();
 258
 0259            if (null == contextId || string.IsNullOrEmpty(endpointId))
 260            {
 0261                return tokens;
 262            }
 263
 264            CacheEntry entry;
 0265            SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(endpointId, contextId, null);
 0266            key.IgnoreKeyGeneration = true;
 267
 0268            lock (_syncRoot)
 269            {
 0270                foreach (SessionSecurityTokenCacheKey itemKey in _items.Keys)
 271                {
 0272                    if (itemKey.Equals(key))
 273                    {
 0274                        entry = _items[itemKey];
 275
 276                        // Move the node to the head of the MRU list if it's not already there
 0277                        if (_mruList.Count > 1 && !object.ReferenceEquals(_mruList.First, entry.Node))
 278                        {
 0279                            _mruList.Remove(entry.Node);
 0280                            _mruList.AddFirst(entry.Node);
 0281                            _mruEntry = entry;
 282                        }
 283
 0284                        tokens.Add(entry.Value);
 285                    }
 286                }
 287            }
 288
 0289            return tokens;
 290        }
 291
 292        /// <summary>
 293        /// This method must not be called from within a read or writer lock as a deadlock will occur.
 294        /// Checks the time a decides if a cleanup needs to occur.
 295        /// </summary>
 296        private void Purge()
 297        {
 0298            if (_items.Count >= MaximumSize)
 299            {
 300                // If the cache is full, purge enough LRU items to shrink the
 301                // cache down to the low watermark
 0302                int countToPurge = MaximumSize - _sizeAfterPurge;
 0303                for (int i = 0; i < countToPurge; i++)
 304                {
 0305                    SessionSecurityTokenCacheKey keyRemove = _mruList.Last.Value;
 0306                    _mruList.RemoveLast();
 0307                    _items.Remove(keyRemove);
 308                }
 309
 310                //if (DiagnosticUtility.ShouldTrace(TraceEventType.Information))
 311                //{
 312                //    TraceUtility.TraceString(
 313                //        TraceEventType.Information,
 314                //        SR.Format(
 315                //        SR.ID8003,
 316                //        this.maximumSize,
 317                //        this.sizeAfterPurge));
 318                //}
 319            }
 0320        }
 321
 322        public class CacheEntry
 323        {
 324            public SessionSecurityToken Value
 325            {
 0326                get;
 0327                set;
 328            }
 329
 330            public LinkedListNode<SessionSecurityTokenCacheKey> Node
 331            {
 0332                get;
 0333                set;
 334            }
 335        }
 336    }
 337}