public Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken) { if (_proxy == null) { return(SendAsyncCore(request, null, doRequestAuth, isProxyConnect: false, cancellationToken)); } // Do proxy lookup. Uri?proxyUri = null; try { Debug.Assert(request.RequestUri != null); if (!_proxy.IsBypassed(request.RequestUri)) { if (_proxy is IMultiWebProxy multiWebProxy) { MultiProxy multiProxy = multiWebProxy.GetMultiProxy(request.RequestUri); if (multiProxy.ReadNext(out proxyUri, out bool isFinalProxy) && !isFinalProxy) { return(SendAsyncMultiProxy(request, doRequestAuth, multiProxy, proxyUri, cancellationToken)); } } else { proxyUri = _proxy.GetProxy(request.RequestUri); } } } catch (Exception ex) { // Eat any exception from the IWebProxy and just treat it as no proxy. // This matches the behavior of other handlers. if (NetEventSource.IsEnabled) { NetEventSource.Error(this, $"Exception from {_proxy.GetType().Name}.GetProxy({request.RequestUri}): {ex}"); } } if (proxyUri != null && proxyUri.Scheme != UriScheme.Http) { throw new NotSupportedException(SR.net_http_invalid_proxy_scheme); } return(SendAsyncCore(request, proxyUri, doRequestAuth, isProxyConnect: false, cancellationToken)); }
private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle?sessionHandle) { _proxyHelper = proxyHelper; _sessionHandle = sessionHandle; if (proxyHelper.ManualSettingsUsed) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(proxyHelper, $"ManualSettingsUsed, {proxyHelper.Proxy}"); } _secureProxy = MultiProxy.Parse(_failedProxies, proxyHelper.Proxy, true); _insecureProxy = MultiProxy.Parse(_failedProxies, proxyHelper.Proxy, false); if (!string.IsNullOrWhiteSpace(proxyHelper.ProxyBypass)) { int idx = 0; string?tmp; // Process bypass list for manual setting. // Initial list size is best guess based on string length assuming each entry is at least 5 characters on average. _bypass = new List <string>(proxyHelper.ProxyBypass.Length / 5); while (idx < proxyHelper.ProxyBypass.Length) { // Strip leading spaces and scheme if any. while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ' ') { idx += 1; } ; if (string.Compare(proxyHelper.ProxyBypass, idx, "http://", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) { idx += 7; } else if (string.Compare(proxyHelper.ProxyBypass, idx, "https://", 0, 8, StringComparison.OrdinalIgnoreCase) == 0) { idx += 8; } if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == '[') { // Strip [] from IPv6 so we can use IdnHost laster for matching. idx += 1; } int start = idx; while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ' ' && proxyHelper.ProxyBypass[idx] != ';' && proxyHelper.ProxyBypass[idx] != ']') { idx += 1; } ; if (idx == start) { // Empty string. tmp = null; } else if (string.Compare(proxyHelper.ProxyBypass, start, "<local>", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) { _bypassLocal = true; tmp = null; } else { tmp = proxyHelper.ProxyBypass.Substring(start, idx - start); } // Skip trailing characters if any. if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';') { // Got stopped at space or ']'. Strip until next ';' or end. while (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] != ';') { idx += 1; } ; } if (idx < proxyHelper.ProxyBypass.Length && proxyHelper.ProxyBypass[idx] == ';') { idx++; } if (tmp == null) { continue; } _bypass.Add(tmp); } if (_bypass.Count == 0) { // Bypass string only had garbage we did not parse. _bypass = null; } } if (_bypassLocal) { _localIp = new List <IPAddress>(); foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) { IPInterfaceProperties ipProps = netInterface.GetIPProperties(); foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) { _localIp.Add(addr.Address); } } } } }
/// <summary> /// Gets the proxy URIs. /// </summary> public MultiProxy GetMultiProxy(Uri uri) { // We need WinHTTP to detect and/or process a PAC (JavaScript) file. This maps to // "Automatically detect settings" and/or "Use automatic configuration script" from IE // settings. But, calling into WinHTTP can be slow especially when it has to call into // the out-of-process service to discover, load, and run the PAC file. So, we skip // calling into WinHTTP if there was a recent failure to detect a PAC file on the network. // This is a common error. The default IE settings on a Windows machine consist of the // single checkbox for "Automatically detect settings" turned on and most networks // won't actually discover a PAC file on the network since WPAD protocol isn't configured. if (_proxyHelper.AutoSettingsUsed && !_proxyHelper.RecentAutoDetectionFailure) { Interop.WinHttp.WINHTTP_PROXY_INFO proxyInfo = default; try { if (_proxyHelper.GetProxyForUrl(_sessionHandle, uri, out proxyInfo)) { // If WinHTTP just specified a Proxy with no ProxyBypass list, then // we can return the Proxy uri directly. if (proxyInfo.ProxyBypass == IntPtr.Zero) { if (proxyInfo.Proxy != IntPtr.Zero) { string proxyStr = Marshal.PtrToStringUni(proxyInfo.Proxy) !; return(MultiProxy.CreateLazy(_failedProxies, proxyStr, IsSecureUri(uri))); } else { return(MultiProxy.Empty); } } // A bypass list was also specified. This means that WinHTTP has fallen back to // using the manual IE settings specified and there is a ProxyBypass list also. // Since we're not really using the full WinHTTP stack, we need to use HttpSystemProxy // to do the computation of the final proxy uri merging the information from the Proxy // and ProxyBypass strings. } else { return(MultiProxy.Empty); } } finally { Marshal.FreeHGlobal(proxyInfo.Proxy); Marshal.FreeHGlobal(proxyInfo.ProxyBypass); } } // Fallback to manual settings if present. if (_proxyHelper.ManualSettingsUsed) { if (_bypassLocal) { IPAddress?address; if (uri.IsLoopback) { // This is optimization for loopback addresses. // Unfortunately this does not work for all local addresses. return(MultiProxy.Empty); } // Pre-Check if host may be IP address to avoid parsing. if (uri.HostNameType == UriHostNameType.IPv6 || uri.HostNameType == UriHostNameType.IPv4) { // RFC1123 allows labels to start with number. // Leading number may or may not be IP address. // IPv6 [::1] notation. '[' is not valid character in names. if (IPAddress.TryParse(uri.IdnHost, out address)) { // Host is valid IP address. // Check if it belongs to local system. foreach (IPAddress a in _localIp !) { if (a.Equals(address)) { return(MultiProxy.Empty); } } } } if (uri.HostNameType != UriHostNameType.IPv6 && !uri.IdnHost.Contains('.')) { // Not address and does not have a dot. // Hosts without FQDN are considered local. return(MultiProxy.Empty); } } // Check if we have other rules for bypass. if (_bypass != null) { foreach (string entry in _bypass) { // IdnHost does not have []. if (SimpleRegex.IsMatchWithStarWildcard(uri.IdnHost, entry)) { return(MultiProxy.Empty); } } } // We did not find match on bypass list. return(IsSecureUri(uri) ? _secureProxy : _insecureProxy); } return(MultiProxy.Empty); }
/// <summary> /// Iterates a request over a set of proxies until one works, or all proxies have failed. /// </summary> /// <param name="multiProxy">The set of proxies to use.</param> /// <param name="firstProxy">The first proxy try.</param> private async Task <HttpResponseMessage> SendAsyncMultiProxy(HttpRequestMessage request, bool doRequestAuth, MultiProxy multiProxy, Uri firstProxy, CancellationToken cancellationToken) { HttpRequestException rethrowException = null; do { try { return(await SendAsyncCore(request, firstProxy, doRequestAuth, isProxyConnect : false, cancellationToken).ConfigureAwait(false)); } catch (HttpRequestException ex) when(ex.AllowRetry != RequestRetryType.NoRetry) { rethrowException = ex; } }while (multiProxy.ReadNext(out firstProxy, out _)); ExceptionDispatchInfo.Throw(rethrowException); return(null); // should never be reached: VS doesn't realize Throw() never returns. }