< Summary

Information
Class: System.Net.Http.HttpEnvironmentProxyCredentials
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\HttpEnvironmentProxy.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 55
Coverable lines: 55
Total lines: 308
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%
GetCredential(...)0%660%
TryCreate(...)0%880%
GetCredentialsFromString(...)0%880%

File(s)

D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\HttpEnvironmentProxy.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
 4namespace System.Net.Http
 5{
 6    internal sealed class HttpEnvironmentProxyCredentials : ICredentials
 7    {
 8        // Wrapper class for cases when http and https has different authentication.
 9        private readonly NetworkCredential? _httpCred;
 10        private readonly NetworkCredential? _httpsCred;
 11        private readonly Uri? _httpProxy;
 12        private readonly Uri? _httpsProxy;
 13
 014        public HttpEnvironmentProxyCredentials(Uri? httpProxy, NetworkCredential? httpCred,
 015                                                Uri? httpsProxy, NetworkCredential? httpsCred)
 016        {
 017            _httpCred = httpCred;
 018            _httpsCred = httpsCred;
 019            _httpProxy = httpProxy;
 020            _httpsProxy = httpsProxy;
 021        }
 22
 23        public NetworkCredential? GetCredential(Uri? uri, string authType)
 024        {
 025            if (uri == null)
 026            {
 027                return null;
 28            }
 029            return uri.Equals(_httpProxy) ? _httpCred :
 030                   uri.Equals(_httpsProxy) ? _httpsCred : null;
 031        }
 32
 33        public static HttpEnvironmentProxyCredentials? TryCreate(Uri? httpProxy, Uri? httpsProxy)
 034        {
 035            NetworkCredential? httpCred = null;
 036            NetworkCredential? httpsCred = null;
 37
 038            if (httpProxy != null)
 039            {
 040                httpCred = GetCredentialsFromString(httpProxy.UserInfo);
 041            }
 042            if (httpsProxy != null)
 043            {
 044                httpsCred = GetCredentialsFromString(httpsProxy.UserInfo);
 045            }
 046            if (httpCred == null && httpsCred == null)
 047            {
 048                return null;
 49            }
 050            return new HttpEnvironmentProxyCredentials(httpProxy, httpCred, httpsProxy, httpsCred);
 051        }
 52
 53        /// <summary>
 54        /// Converts string containing user:password to NetworkCredential object
 55        /// </summary>
 56        private static NetworkCredential? GetCredentialsFromString(string? value)
 057        {
 058            if (string.IsNullOrWhiteSpace(value))
 059            {
 060                return null;
 61            }
 62
 063            if (value == ":")
 064            {
 065                return CredentialCache.DefaultNetworkCredentials;
 66            }
 67
 068            value = Uri.UnescapeDataString(value);
 69
 070            string password = "";
 071            string? domain = null;
 072            int idx = value.IndexOf(':');
 073            if (idx != -1)
 074            {
 075                password = value.Substring(idx + 1);
 076                value = value.Substring(0, idx);
 077            }
 78
 079            idx = value.IndexOf('\\');
 080            if (idx != -1)
 081            {
 082                domain = value.Substring(0, idx);
 083                value = value.Substring(idx + 1);
 084            }
 85
 086            return new NetworkCredential(value, password, domain);
 087        }
 88    }
 89
 90    internal sealed partial class HttpEnvironmentProxy : IWebProxy
 91    {
 92        private const string EnvAllProxyUC = "ALL_PROXY";
 93        private const string EnvHttpProxyUC = "HTTP_PROXY";
 94        private const string EnvHttpsProxyUC = "HTTPS_PROXY";
 95        private const string EnvNoProxyUC = "NO_PROXY";
 96        private const string EnvCGI = "GATEWAY_INTERFACE"; // Running in a CGI environment.
 97
 98        private readonly Uri? _httpProxyUri;       // String URI for HTTP requests
 99        private readonly Uri? _httpsProxyUri;      // String URI for HTTPS requests
 100        private readonly string[]? _bypass;        // list of domains not to proxy
 101        private ICredentials? _credentials;
 102
 103        private HttpEnvironmentProxy(Uri? httpProxy, Uri? httpsProxy, string? bypassList)
 104        {
 105            _httpProxyUri = httpProxy;
 106            _httpsProxyUri = httpsProxy;
 107
 108            _credentials = HttpEnvironmentProxyCredentials.TryCreate(httpProxy, httpsProxy);
 109            _bypass = bypassList?.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
 110        }
 111
 112        /// <summary>
 113        /// Attempt to parse a partial Uri string into a Uri object.
 114        /// The string may contain the scheme, user info, host, and port. The host is the only required part.
 115        /// Example expected inputs: contoso.com, contoso.com:8080, http://contoso.com/, user@contoso.com.
 116        /// </summary>
 117        /// <returns><see langword="null"/> if parsing fails.</returns>
 118        private static Uri? GetUriFromString(string? value)
 119        {
 120            if (string.IsNullOrEmpty(value))
 121            {
 122                return null;
 123            }
 124
 125            int hostIndex = 0;
 126            string protocol = "http";
 127            ushort port = 80;
 128
 129            if (value.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
 130            {
 131                hostIndex = 7;
 132            }
 133            else if (value.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
 134            {
 135                hostIndex = 8;
 136                protocol = "https";
 137                port = 443;
 138            }
 139            else if (value.StartsWith("socks4://", StringComparison.OrdinalIgnoreCase))
 140            {
 141                hostIndex = 9;
 142                protocol = "socks4";
 143            }
 144            else if (value.StartsWith("socks5://", StringComparison.OrdinalIgnoreCase))
 145            {
 146                hostIndex = 9;
 147                protocol = "socks5";
 148            }
 149            else if (value.StartsWith("socks4a://", StringComparison.OrdinalIgnoreCase))
 150            {
 151                hostIndex = 10;
 152                protocol = "socks4a";
 153            }
 154
 155            if (hostIndex > 0)
 156            {
 157                value = value.Substring(hostIndex);
 158            }
 159
 160            string? user = null;
 161            string? password = null;
 162            string host;
 163
 164            // Check if there is authentication part with user and possibly password.
 165            // Curl accepts raw text and that may break URI parser.
 166            int separatorIndex = value.LastIndexOf('@');
 167            if (separatorIndex != -1)
 168            {
 169                // The User and password may or may not be URL encoded.
 170                // Curl seems to accept both. To match that, we also decode the value.
 171                string auth = Uri.UnescapeDataString(value.AsSpan(0, separatorIndex));
 172
 173                value = value.Substring(separatorIndex + 1);
 174                separatorIndex = auth.IndexOf(':');
 175                if (separatorIndex == -1)
 176                {
 177                    user = auth;
 178                }
 179                else
 180                {
 181                    user = auth.Substring(0, separatorIndex);
 182                    password = auth.Substring(separatorIndex + 1);
 183                }
 184            }
 185
 186            // We expect inputs to not contain the path/query/fragment, but we do handle some simple cases like a traili
 187            // An arbitrary path can break this parsing logic, but we expect environment variables to be trusted and wel
 188            int delimiterIndex = value.IndexOfAny('/', '?', '#');
 189            if (delimiterIndex >= 0)
 190            {
 191                value = value.Substring(0, delimiterIndex);
 192            }
 193
 194            int ipV6AddressEnd = value.IndexOf(']');
 195            separatorIndex = value.LastIndexOf(':');
 196            // No ':' or it is part of IPv6 address.
 197            if (separatorIndex == -1 || (ipV6AddressEnd != -1 && separatorIndex < ipV6AddressEnd))
 198            {
 199                host = value;
 200            }
 201            else
 202            {
 203                host = value.Substring(0, separatorIndex);
 204
 205                if (!ushort.TryParse(value.AsSpan(separatorIndex + 1), out port))
 206                {
 207                    return null;
 208                }
 209            }
 210
 211            try
 212            {
 213                UriBuilder ub = new UriBuilder(protocol, host, port);
 214                if (user != null)
 215                {
 216                    ub.UserName = Uri.EscapeDataString(user);
 217                }
 218
 219                if (password != null)
 220                {
 221                    ub.Password = Uri.EscapeDataString(password);
 222                }
 223
 224                Uri uri = ub.Uri;
 225
 226                // if both user and password exist and are empty we should preserve that and use default credentials.
 227                // UriBuilder does not handle that now e.g. does not distinguish between empty and missing.
 228                if (user == "" && password == "")
 229                {
 230                    Span<Range> tokens = stackalloc Range[3];
 231                    ReadOnlySpan<char> uriSpan = uri.ToString();
 232                    if (uriSpan.Split(tokens, '/') == 3)
 233                    {
 234                        uri = new Uri($"{uriSpan[tokens[0]]}//:@{uriSpan[tokens[2]]}");
 235                    }
 236                }
 237
 238                return uri;
 239            }
 240            catch { };
 241            return null;
 242        }
 243
 244        /// <summary>
 245        /// This function returns true if given Host match bypass list.
 246        /// Note, that the list is common for http and https.
 247        /// </summary>
 248        private bool IsMatchInBypassList(Uri input)
 249        {
 250            if (_bypass != null)
 251            {
 252                foreach (string s in _bypass)
 253                {
 254                    if (s[0] == '.')
 255                    {
 256                        // This should match either domain it self or any subdomain or host
 257                        // .foo.com will match foo.com it self or *.foo.com
 258                        if (s.AsSpan(1).Equals(input.Host, StringComparison.OrdinalIgnoreCase))
 259                        {
 260                            return true;
 261                        }
 262                        else if (input.Host.EndsWith(s, StringComparison.OrdinalIgnoreCase))
 263                        {
 264                            return true;
 265                        }
 266
 267                    }
 268                    else
 269                    {
 270                        if (string.Equals(s, input.Host, StringComparison.OrdinalIgnoreCase))
 271                        {
 272                            return true;
 273                        }
 274                    }
 275                }
 276            }
 277            return false;
 278        }
 279
 280        /// <summary>
 281        /// Gets the proxy URI. (iWebProxy interface)
 282        /// </summary>
 283        public Uri? GetProxy(Uri uri)
 284        {
 285            return HttpUtilities.IsSupportedNonSecureScheme(uri.Scheme) ? _httpProxyUri : _httpsProxyUri;
 286        }
 287
 288        /// <summary>
 289        /// Checks if URI is subject to proxy or not.
 290        /// </summary>
 291        public bool IsBypassed(Uri uri)
 292        {
 293            return GetProxy(uri) == null ? true : IsMatchInBypassList(uri);
 294        }
 295
 296        public ICredentials? Credentials
 297        {
 298            get
 299            {
 300                return _credentials;
 301            }
 302            set
 303            {
 304                _credentials = value;
 305            }
 306        }
 307    }
 308}