< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 45
Coverable lines: 45
Total lines: 157
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 22
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%
Match(...)0%880%
IsPrefix(...)0%880%
GetHashCode()100%110%
Equals(...)0%660%
Equals(...)100%110%
ToString()100%110%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/Common/src/System/Net/CredentialCacheKey.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.Globalization;
 9
 10namespace System.Net
 11{
 12    internal sealed class CredentialCacheKey : IEquatable<CredentialCacheKey?>
 13    {
 14        public readonly Uri UriPrefix;
 015        public readonly int UriPrefixLength = -1;
 16        public readonly string AuthenticationType;
 17
 018        internal CredentialCacheKey(Uri uriPrefix, string authenticationType)
 019        {
 020            Debug.Assert(uriPrefix != null);
 021            Debug.Assert(authenticationType != null);
 22
 023            UriPrefix = uriPrefix;
 024            UriPrefixLength = UriPrefix.AbsolutePath.LastIndexOf('/');
 025            AuthenticationType = authenticationType;
 026        }
 27
 28        internal bool Match(Uri uri, int prefixLen, string authenticationType)
 029        {
 030            if (uri == null || authenticationType == null)
 031            {
 032                return false;
 33            }
 34
 35            // If the protocols don't match, this credential is not applicable for the given Uri.
 036            if (!string.Equals(authenticationType, AuthenticationType, StringComparison.OrdinalIgnoreCase))
 037            {
 038                return false;
 39            }
 40
 041            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Match({UriPrefix} & {uri})");
 42
 043            return IsPrefix(uri, prefixLen);
 044        }
 45
 46        // IsPrefix (Uri)
 47        //
 48        // Determines whether <this> is a prefix of this URI. A prefix
 49        // match is defined as:
 50        //
 51        //     scheme match
 52        //     + host match
 53        //     + port match, if any
 54        //     + <prefix> path is a prefix of <URI> path, if any
 55        //
 56        // Returns:
 57        // True if <prefixUri> is a prefix of this URI
 58        private bool IsPrefix(Uri uri, int prefixLen)
 059        {
 060            Debug.Assert(uri != null);
 061            Uri uriPrefix = UriPrefix;
 62
 063            if (uriPrefix.Scheme != uri.Scheme || uriPrefix.Host != uri.Host || uriPrefix.Port != uri.Port)
 064            {
 065                return false;
 66            }
 67
 068            if (UriPrefixLength > prefixLen)
 069            {
 070                return false;
 71            }
 72
 73            // Compare the path prefix including the trailing '/' delimiter (UriPrefixLength points at
 74            // the last '/' in the prefix path). Including that delimiter ensures the prefix only matches
 75            // at a path-segment boundary, so a prefix such as "/admin/" does not incorrectly match a
 76            // sibling path like "/administrator/...".
 77            //
 78            // The path on the server may be case-sensitive, so the path portion is compared
 79            // case-sensitively (scheme, host, and port are already matched above).
 080            int compareLength = UriPrefixLength + 1;
 081            return uri.AbsolutePath.AsSpan(0, compareLength).Equals(uriPrefix.AbsolutePath.AsSpan(0, compareLength), Str
 082        }
 83
 84        public override int GetHashCode() =>
 085            StringComparer.OrdinalIgnoreCase.GetHashCode(AuthenticationType) ^
 086            UriPrefix.GetHashCode();
 87
 88        public bool Equals([NotNullWhen(true)] CredentialCacheKey? other)
 089        {
 090            if (other == null)
 091            {
 092                return false;
 93            }
 94
 095            bool equals =
 096                string.Equals(AuthenticationType, other.AuthenticationType, StringComparison.OrdinalIgnoreCase) &&
 097                UriPrefix.Equals(other.UriPrefix);
 98
 099            if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Equals({this},{other}) returns {equals}");
 100
 0101            return equals;
 0102        }
 103
 0104        public override bool Equals([NotNullWhen(true)] object? obj) => Equals(obj as CredentialCacheKey);
 105
 106        public override string ToString() =>
 0107            string.Create(CultureInfo.InvariantCulture, $"[{UriPrefixLength}]:{UriPrefix}:{AuthenticationType}");
 108    }
 109
 110    internal static class CredentialCacheHelper
 111    {
 112        public static bool TryGetCredential(Dictionary<CredentialCacheKey, NetworkCredential> cache, Uri uriPrefix, stri
 113        {
 114            int longestMatchPrefix = -1;
 115            mostSpecificMatch = null;
 116            mostSpecificMatchUri = null;
 117
 118            if (cache.Count == 0)
 119            {
 120                return false;
 121            }
 122
 123            // precompute the length of the prefix
 124            int uriPrefixLength = uriPrefix.AbsolutePath.LastIndexOf('/');
 125
 126            // Enumerate through every credential in the cache, get match with longest prefix
 127            foreach ((CredentialCacheKey key, NetworkCredential value) in cache)
 128            {
 129                int prefixLen = key.UriPrefixLength;
 130
 131                if (prefixLen <= longestMatchPrefix)
 132                {
 133                    // this credential can't provide a longer prefix match
 134                    continue;
 135                }
 136
 137                // Determine if this credential is applicable to the current Uri/AuthType
 138                if (key.Match(uriPrefix, uriPrefixLength, authType))
 139                {
 140                    // update the information about currently preferred match
 141                    longestMatchPrefix = prefixLen;
 142                    mostSpecificMatch = value;
 143                    mostSpecificMatchUri = key.UriPrefix;
 144
 145                    if (uriPrefixLength == prefixLen)
 146                    {
 147                        // we can't get any better than this
 148                        break;
 149                    }
 150                }
 151            }
 152
 153            return mostSpecificMatch != null;
 154        }
 155    }
 156}
 157