Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Net / System / Net / NetWebProxyFinder.cs / 1305376 / NetWebProxyFinder.cs
using System.IO; using System.Collections; using System.Collections.Specialized; using System.Threading; using System.Text; using System.Net.Cache; using System.Globalization; using System.Net.Configuration; using System.Security.Permissions; using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.Win32; using System.Diagnostics.CodeAnalysis; namespace System.Net { // This WebProxyFinder implementation has the following purpose: // - use WinHttp APIs to determine the location of the PAC file // - use System.Net classes (WebRequest) to download the PAC file // - use Microsoft.JScript to compile and execute the JavaScript in the PAC file. internal sealed class NetWebProxyFinder : BaseWebProxyFinder { private static readonly char[] splitChars = new char[] { ';' }; private static TimerThread.Queue timerQueue; private static readonly TimerThread.Callback timerCallback = new TimerThread.Callback(RequestTimeoutCallback); private static readonly WaitCallback abortWrapper = new WaitCallback(AbortWrapper); private RequestCache backupCache; private AutoWebProxyScriptWrapper scriptInstance; private Uri engineScriptLocation; private Uri scriptLocation; private bool scriptDetectionFailed; private object lockObject; // Keep the following fields volatile, since we're accessing them outside of lock blocks private volatile WebRequest request; private volatile bool aborted; public NetWebProxyFinder(AutoWebProxyScriptEngine engine) : base(engine) { backupCache = new SingleItemRequestCache(RequestCacheManager.IsCachingEnabled); lockObject = new object(); } public override bool GetProxies(Uri destination, out IListproxyList) { try { proxyList = null; EnsureEngineAvailable(); // after EnsureEngineAvailable we expect State to be CompilationSuccess, otherwise return. if (State != AutoWebProxyState.Completed) { // the script can't run, say we're not ready and bypass return false; } bool result = false; try { string proxyListString = scriptInstance.FindProxyForURL(destination.ToString(), destination.Host); GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::GetProxies() calling ExecuteFindProxyForURL() for destination:" + ValidationHelper.ToString(destination) + " returned scriptReturn:" + ValidationHelper.ToString(proxyList)); proxyList = ParseScriptResult(proxyListString); result = true; } catch (Exception exception) { if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_script_execution_error, exception)); } return result; } finally { // Reset state of 'aborted', since next call to GetProxies() must not use previous aborted state. aborted = false; } } public override void Abort() { // All we abort is a running WebRequest. The following lock (and the one in DownloadAndCompile) // is used to "atomically" access the two fields 'aborted' and 'request': If Abort() gets // called before 'request' is set, the 'aborted' field will signal to DownloadAndCompile, that // it should not bother creating a request and just throw. If 'request' was already created // by DownloadAndCompile, the following code will make sure the request gets aborted. lock (lockObject) { aborted = true; if (request != null) { ThreadPool.UnsafeQueueUserWorkItem(abortWrapper, request); } } } protected override void Dispose(bool disposing) { if (disposing) { if (scriptInstance != null) { scriptInstance.Close(); } } } // Ensures that (if state is AutoWebProxyState.CompilationSuccess) there is an engine available to execute script. // Figures out the script location (might discover if needed). // Calls DownloadAndCompile(). private void EnsureEngineAvailable() { GlobalLog.Enter("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable"); if (State == AutoWebProxyState.Uninitialized || engineScriptLocation == null) { #if !FEATURE_PAL if (Engine.AutomaticallyDetectSettings) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() Attempting auto-detection."); DetectScriptLocation(); if (scriptLocation != null) { // // Successfully detected or user has flipped the automaticallyDetectSettings bit. // Attempt a non conclusive DownloadAndCompile() so we can fallback // GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() discovered:" + ValidationHelper.ToString(scriptLocation) + " engineScriptLocation:" + ValidationHelper.ToString(engineScriptLocation)); if (scriptLocation.Equals(engineScriptLocation)) { State = AutoWebProxyState.Completed; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } AutoWebProxyState newState = DownloadAndCompile(scriptLocation); if (newState == AutoWebProxyState.Completed) { State = AutoWebProxyState.Completed; engineScriptLocation = scriptLocation; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } } } #endif // !FEATURE_PAL // Either Auto-Detect wasn't enabled or something failed with it. Try the manual script location. if ((Engine.AutomaticConfigurationScript != null) && !aborted) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() using automaticConfigurationScript:" + ValidationHelper.ToString(Engine.AutomaticConfigurationScript) + " engineScriptLocation:" + ValidationHelper.ToString(engineScriptLocation)); if (Engine.AutomaticConfigurationScript.Equals(engineScriptLocation)) { State = AutoWebProxyState.Completed; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } State = DownloadAndCompile(Engine.AutomaticConfigurationScript); if (State == AutoWebProxyState.Completed) { engineScriptLocation = Engine.AutomaticConfigurationScript; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } } } else { // We always want to call DownloadAndCompile to check the expiration. GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() State:" + State + " engineScriptLocation:" + ValidationHelper.ToString(engineScriptLocation)); State = DownloadAndCompile(engineScriptLocation); if (State == AutoWebProxyState.Completed) { GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } // There's still an opportunity to fail over to the automaticConfigurationScript. if (!engineScriptLocation.Equals(Engine.AutomaticConfigurationScript) && !aborted) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() Update failed. Falling back to automaticConfigurationScript:" + ValidationHelper.ToString(Engine.AutomaticConfigurationScript)); State = DownloadAndCompile(Engine.AutomaticConfigurationScript); if (State == AutoWebProxyState.Completed) { engineScriptLocation = Engine.AutomaticConfigurationScript; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } } } // Everything failed. Set this instance to mostly-dead. It will wake up again if there's a reg/connectoid change. GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() All failed."); State = AutoWebProxyState.DiscoveryFailure; if (scriptInstance != null) { scriptInstance.Close(); scriptInstance = null; } engineScriptLocation = null; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); } // Downloads and compiles the script from a given Uri. // This code can be called by config for a downloaded control, we need to assert. // This code is called holding the lock. private AutoWebProxyState DownloadAndCompile(Uri location) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() location:" + ValidationHelper.ToString(location)); AutoWebProxyState newState = AutoWebProxyState.DownloadFailure; WebResponse response = null; TimerThread.Timer timer = null; AutoWebProxyScriptWrapper newScriptInstance = null; // Can't assert this in declarative form (DCR?). This Assert() is needed to be able to create the request to download the proxy script. ExceptionHelper.WebPermissionUnrestricted.Assert(); try { lock (lockObject) { if (aborted) { throw new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled); } request = WebRequest.Create(location); } request.Timeout = Timeout.Infinite; request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Default); request.ConnectionGroupName = "__WebProxyScript"; // We have an opportunity here, if caching is disabled AppDomain-wide, to override it with a // custom, trivial cache-provider to get a similar semantic. // // We also want to have a backup caching key in the case when IE has locked an expired script response // if (request.CacheProtocol != null) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() Using backup caching."); request.CacheProtocol = new RequestCacheProtocol(backupCache, request.CacheProtocol.Validator); } HttpWebRequest httpWebRequest = request as HttpWebRequest; if (httpWebRequest != null) { httpWebRequest.Accept = "*/*"; httpWebRequest.UserAgent = this.GetType().FullName + "/" + Environment.Version; httpWebRequest.KeepAlive = false; httpWebRequest.Pipelined = false; httpWebRequest.InternalConnectionGroup = true; } else { FtpWebRequest ftpWebRequest = request as FtpWebRequest; if (ftpWebRequest != null) { ftpWebRequest.KeepAlive = false; } } // Use no proxy, default cache - initiate the download. request.Proxy = null; request.Credentials = Engine.Credentials; // Use our own timeout timer so that it can encompass the whole request, not just the headers. if (timerQueue == null) { timerQueue = TimerThread.GetOrCreateQueue(SettingsSectionInternal.Section.DownloadTimeout); } timer = timerQueue.CreateTimer(timerCallback, request); response = request.GetResponse(); // Check Last Modified. DateTime lastModified = DateTime.MinValue; HttpWebResponse httpResponse = response as HttpWebResponse; if (httpResponse != null) { lastModified = httpResponse.LastModified; } else { FtpWebResponse ftpResponse = response as FtpWebResponse; if (ftpResponse != null) { lastModified = ftpResponse.LastModified; } } GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() lastModified:" + lastModified.ToString() + " (script):" + (scriptInstance == null ? "(null)" : scriptInstance.LastModified.ToString())); if (scriptInstance != null && lastModified != DateTime.MinValue && scriptInstance.LastModified == lastModified) { newScriptInstance = scriptInstance; newState = AutoWebProxyState.Completed; } else { string scriptBody = null; byte[] scriptBuffer = null; using (Stream responseStream = response.GetResponseStream()) { SingleItemRequestCache.ReadOnlyStream ros = responseStream as SingleItemRequestCache.ReadOnlyStream; if (ros != null) { scriptBuffer = ros.Buffer; } if (scriptInstance != null && scriptBuffer != null && scriptBuffer == scriptInstance.Buffer) { scriptInstance.LastModified = lastModified; newScriptInstance = scriptInstance; newState = AutoWebProxyState.Completed; GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() Buffer matched - reusing Engine."); } else { using (StreamReader streamReader = new StreamReader(responseStream)) { scriptBody = streamReader.ReadToEnd(); } } } WebResponse tempResponse = response; response = null; tempResponse.Close(); timer.Cancel(); timer = null; if (newState != AutoWebProxyState.Completed) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() IsFromCache:" + tempResponse.IsFromCache.ToString() + " scriptInstance:" + ValidationHelper.HashString(scriptInstance)); if (scriptInstance != null && scriptBody == scriptInstance.ScriptBody) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() Script matched - using existing Engine."); scriptInstance.LastModified = lastModified; if (scriptBuffer != null) { scriptInstance.Buffer = scriptBuffer; } newScriptInstance = scriptInstance; newState = AutoWebProxyState.Completed; } else { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() Creating AutoWebProxyScriptWrapper."); newScriptInstance = new AutoWebProxyScriptWrapper(); newScriptInstance.LastModified = lastModified; if (newScriptInstance.Compile(location, scriptBody, scriptBuffer)) { newState = AutoWebProxyState.Completed; } else { newState = AutoWebProxyState.CompilationFailure; } } } } } catch (Exception exception) { if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_script_download_compile_error, exception)); GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() Download() threw:" + ValidationHelper.ToString(exception)); } finally { if (timer != null) { timer.Cancel(); } // try { if (response != null) { response.Close(); } } finally { WebPermission.RevertAssert(); // The request is not needed anymore. Set it to null, so if Abort() gets called, // after this point, it will result in a no-op. request = null; } } if ((newState == AutoWebProxyState.Completed) && (scriptInstance != newScriptInstance)) { if (scriptInstance != null) { scriptInstance.Close(); } scriptInstance = newScriptInstance; } GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() retuning newState:" + ValidationHelper.ToString(newState)); return newState; } private static IList ParseScriptResult(string scriptReturn) { IList result = new List (); if (scriptReturn == null) { return result; } string[] proxyListStrings = scriptReturn.Split(splitChars); string proxyAuthority; foreach (string s in proxyListStrings) { string proxyString = s.Trim(' '); if (!proxyString.StartsWith("PROXY ", StringComparison.OrdinalIgnoreCase)) { if (string.Compare("DIRECT", proxyString, StringComparison.OrdinalIgnoreCase) == 0) { proxyAuthority = null; } else { continue; } } else { // remove prefix "PROXY " (6 chars) from the string and trim additional leading spaces. proxyAuthority = proxyString.Substring(6).TrimStart(' '); Uri uri = null; bool tryParse = Uri.TryCreate("http://" + proxyAuthority, UriKind.Absolute, out uri); if (!tryParse || uri.UserInfo.Length > 0 || uri.HostNameType == UriHostNameType.Basic || uri.AbsolutePath.Length != 1 || proxyAuthority[proxyAuthority.Length - 1] == '/' || proxyAuthority[proxyAuthority.Length - 1] == '#' || proxyAuthority[proxyAuthority.Length - 1] == '?') { continue; } } result.Add(proxyAuthority); } return result; } private void DetectScriptLocation() { if (scriptDetectionFailed || scriptLocation != null) { return; } GlobalLog.Print("NetWebProxyFinder::DetectScriptLocation() Attempting discovery PROXY_AUTO_DETECT_TYPE_DHCP."); scriptLocation = SafeDetectAutoProxyUrl(UnsafeNclNativeMethods.WinHttp.AutoDetectType.Dhcp); if (scriptLocation == null) { GlobalLog.Print("NetWebProxyFinder::DetectScriptLocation() Attempting discovery AUTO_DETECT_TYPE_DNS_A."); scriptLocation = SafeDetectAutoProxyUrl(UnsafeNclNativeMethods.WinHttp.AutoDetectType.DnsA); } if (scriptLocation == null) { GlobalLog.Print("NetWebProxyFinder::DetectScriptLocation() Discovery failed."); scriptDetectionFailed = true; } } // from wininet.h // // #define INTERNET_MAX_PATH_LENGTH 2048 // #define INTERNET_MAX_PROTOCOL_NAME "gopher" // longest protocol name // #define INTERNET_MAX_URL_LENGTH ((sizeof(INTERNET_MAX_PROTOCOL_NAME) - 1) \ // + sizeof("://") \ // + INTERNET_MAX_PATH_LENGTH) // private const int MaximumProxyStringLength = 2058; /// /// [SuppressMessage("Microsoft.Reliability","CA2001:AvoidCallingProblematicMethods", MessageId="System.Runtime.InteropServices.SafeHandle.DangerousGetHandle", Justification="Implementation requires DangerousGetHandle")] private static unsafe Uri SafeDetectAutoProxyUrl( UnsafeNclNativeMethods.WinHttp.AutoDetectType discoveryMethod) { Uri autoProxy = null; #if !FEATURE_PAL string url = null; if (ComNetOS.IsWinHttp51) { GlobalLog.Print("NetWebProxyFinder::SafeDetectAutoProxyUrl() Using WinHttp."); SafeGlobalFree autoProxyUrl; bool success = UnsafeNclNativeMethods.WinHttp.WinHttpDetectAutoProxyConfigUrl(discoveryMethod, out autoProxyUrl); if (!success) { if (autoProxyUrl != null) { autoProxyUrl.SetHandleAsInvalid(); } } else { url = new string((char*)autoProxyUrl.DangerousGetHandle()); autoProxyUrl.Close(); } } else { GlobalLog.Print("NetWebProxyFinder::SafeDetectAutoProxyUrl() Using WinInet."); StringBuilder autoProxyUrl = new StringBuilder(MaximumProxyStringLength); bool success = UnsafeNclNativeMethods.WinInet.DetectAutoProxyUrl( autoProxyUrl, MaximumProxyStringLength, (int)discoveryMethod); if (success) { url = autoProxyUrl.ToString(); } } if (url != null) { bool parsed = Uri.TryCreate(url, UriKind.Absolute, out autoProxy); if (!parsed) { if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_autodetect_script_location_parse_error, ValidationHelper.ToString(url))); GlobalLog.Print("NetWebProxyFinder::SafeDetectAutoProxyUrl() Uri.TryParse() failed url:" + ValidationHelper.ToString(url)); } } else { if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_autodetect_failed)); GlobalLog.Print("NetWebProxyFinder::SafeDetectAutoProxyUrl() DetectAutoProxyUrl() returned false"); } #endif // !FEATURE_PAL return autoProxy; } // RequestTimeoutCallback - Called by the TimerThread to abort a request. This just posts ThreadPool work item - Abort() does too // much to be done on the timer thread (timer thread should never block or call user code). private static void RequestTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context) { ThreadPool.UnsafeQueueUserWorkItem(abortWrapper, context); } private static void AbortWrapper(object context) { #if DEBUG GlobalLog.SetThreadSource(ThreadKinds.Worker); using (GlobalLog.SetThreadKind(ThreadKinds.System)) { #endif if (context != null) { ((WebRequest)context).Abort(); } #if DEBUG } #endif } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using System.IO; using System.Collections; using System.Collections.Specialized; using System.Threading; using System.Text; using System.Net.Cache; using System.Globalization; using System.Net.Configuration; using System.Security.Permissions; using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.Win32; using System.Diagnostics.CodeAnalysis; namespace System.Net { // This WebProxyFinder implementation has the following purpose: // - use WinHttp APIs to determine the location of the PAC file // - use System.Net classes (WebRequest) to download the PAC file // - use Microsoft.JScript to compile and execute the JavaScript in the PAC file. internal sealed class NetWebProxyFinder : BaseWebProxyFinder { private static readonly char[] splitChars = new char[] { ';' }; private static TimerThread.Queue timerQueue; private static readonly TimerThread.Callback timerCallback = new TimerThread.Callback(RequestTimeoutCallback); private static readonly WaitCallback abortWrapper = new WaitCallback(AbortWrapper); private RequestCache backupCache; private AutoWebProxyScriptWrapper scriptInstance; private Uri engineScriptLocation; private Uri scriptLocation; private bool scriptDetectionFailed; private object lockObject; // Keep the following fields volatile, since we're accessing them outside of lock blocks private volatile WebRequest request; private volatile bool aborted; public NetWebProxyFinder(AutoWebProxyScriptEngine engine) : base(engine) { backupCache = new SingleItemRequestCache(RequestCacheManager.IsCachingEnabled); lockObject = new object(); } public override bool GetProxies(Uri destination, out IList/// Called to discover script location. This performs /// autodetection using the method specified in the detectFlags. /// ///proxyList) { try { proxyList = null; EnsureEngineAvailable(); // after EnsureEngineAvailable we expect State to be CompilationSuccess, otherwise return. if (State != AutoWebProxyState.Completed) { // the script can't run, say we're not ready and bypass return false; } bool result = false; try { string proxyListString = scriptInstance.FindProxyForURL(destination.ToString(), destination.Host); GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::GetProxies() calling ExecuteFindProxyForURL() for destination:" + ValidationHelper.ToString(destination) + " returned scriptReturn:" + ValidationHelper.ToString(proxyList)); proxyList = ParseScriptResult(proxyListString); result = true; } catch (Exception exception) { if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_script_execution_error, exception)); } return result; } finally { // Reset state of 'aborted', since next call to GetProxies() must not use previous aborted state. aborted = false; } } public override void Abort() { // All we abort is a running WebRequest. The following lock (and the one in DownloadAndCompile) // is used to "atomically" access the two fields 'aborted' and 'request': If Abort() gets // called before 'request' is set, the 'aborted' field will signal to DownloadAndCompile, that // it should not bother creating a request and just throw. If 'request' was already created // by DownloadAndCompile, the following code will make sure the request gets aborted. lock (lockObject) { aborted = true; if (request != null) { ThreadPool.UnsafeQueueUserWorkItem(abortWrapper, request); } } } protected override void Dispose(bool disposing) { if (disposing) { if (scriptInstance != null) { scriptInstance.Close(); } } } // Ensures that (if state is AutoWebProxyState.CompilationSuccess) there is an engine available to execute script. // Figures out the script location (might discover if needed). // Calls DownloadAndCompile(). private void EnsureEngineAvailable() { GlobalLog.Enter("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable"); if (State == AutoWebProxyState.Uninitialized || engineScriptLocation == null) { #if !FEATURE_PAL if (Engine.AutomaticallyDetectSettings) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() Attempting auto-detection."); DetectScriptLocation(); if (scriptLocation != null) { // // Successfully detected or user has flipped the automaticallyDetectSettings bit. // Attempt a non conclusive DownloadAndCompile() so we can fallback // GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() discovered:" + ValidationHelper.ToString(scriptLocation) + " engineScriptLocation:" + ValidationHelper.ToString(engineScriptLocation)); if (scriptLocation.Equals(engineScriptLocation)) { State = AutoWebProxyState.Completed; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } AutoWebProxyState newState = DownloadAndCompile(scriptLocation); if (newState == AutoWebProxyState.Completed) { State = AutoWebProxyState.Completed; engineScriptLocation = scriptLocation; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } } } #endif // !FEATURE_PAL // Either Auto-Detect wasn't enabled or something failed with it. Try the manual script location. if ((Engine.AutomaticConfigurationScript != null) && !aborted) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() using automaticConfigurationScript:" + ValidationHelper.ToString(Engine.AutomaticConfigurationScript) + " engineScriptLocation:" + ValidationHelper.ToString(engineScriptLocation)); if (Engine.AutomaticConfigurationScript.Equals(engineScriptLocation)) { State = AutoWebProxyState.Completed; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } State = DownloadAndCompile(Engine.AutomaticConfigurationScript); if (State == AutoWebProxyState.Completed) { engineScriptLocation = Engine.AutomaticConfigurationScript; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } } } else { // We always want to call DownloadAndCompile to check the expiration. GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() State:" + State + " engineScriptLocation:" + ValidationHelper.ToString(engineScriptLocation)); State = DownloadAndCompile(engineScriptLocation); if (State == AutoWebProxyState.Completed) { GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } // There's still an opportunity to fail over to the automaticConfigurationScript. if (!engineScriptLocation.Equals(Engine.AutomaticConfigurationScript) && !aborted) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() Update failed. Falling back to automaticConfigurationScript:" + ValidationHelper.ToString(Engine.AutomaticConfigurationScript)); State = DownloadAndCompile(Engine.AutomaticConfigurationScript); if (State == AutoWebProxyState.Completed) { engineScriptLocation = Engine.AutomaticConfigurationScript; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); return; } } } // Everything failed. Set this instance to mostly-dead. It will wake up again if there's a reg/connectoid change. GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable() All failed."); State = AutoWebProxyState.DiscoveryFailure; if (scriptInstance != null) { scriptInstance.Close(); scriptInstance = null; } engineScriptLocation = null; GlobalLog.Leave("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::EnsureEngineAvailable", ValidationHelper.ToString(State)); } // Downloads and compiles the script from a given Uri. // This code can be called by config for a downloaded control, we need to assert. // This code is called holding the lock. private AutoWebProxyState DownloadAndCompile(Uri location) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() location:" + ValidationHelper.ToString(location)); AutoWebProxyState newState = AutoWebProxyState.DownloadFailure; WebResponse response = null; TimerThread.Timer timer = null; AutoWebProxyScriptWrapper newScriptInstance = null; // Can't assert this in declarative form (DCR?). This Assert() is needed to be able to create the request to download the proxy script. ExceptionHelper.WebPermissionUnrestricted.Assert(); try { lock (lockObject) { if (aborted) { throw new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled); } request = WebRequest.Create(location); } request.Timeout = Timeout.Infinite; request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Default); request.ConnectionGroupName = "__WebProxyScript"; // We have an opportunity here, if caching is disabled AppDomain-wide, to override it with a // custom, trivial cache-provider to get a similar semantic. // // We also want to have a backup caching key in the case when IE has locked an expired script response // if (request.CacheProtocol != null) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() Using backup caching."); request.CacheProtocol = new RequestCacheProtocol(backupCache, request.CacheProtocol.Validator); } HttpWebRequest httpWebRequest = request as HttpWebRequest; if (httpWebRequest != null) { httpWebRequest.Accept = "*/*"; httpWebRequest.UserAgent = this.GetType().FullName + "/" + Environment.Version; httpWebRequest.KeepAlive = false; httpWebRequest.Pipelined = false; httpWebRequest.InternalConnectionGroup = true; } else { FtpWebRequest ftpWebRequest = request as FtpWebRequest; if (ftpWebRequest != null) { ftpWebRequest.KeepAlive = false; } } // Use no proxy, default cache - initiate the download. request.Proxy = null; request.Credentials = Engine.Credentials; // Use our own timeout timer so that it can encompass the whole request, not just the headers. if (timerQueue == null) { timerQueue = TimerThread.GetOrCreateQueue(SettingsSectionInternal.Section.DownloadTimeout); } timer = timerQueue.CreateTimer(timerCallback, request); response = request.GetResponse(); // Check Last Modified. DateTime lastModified = DateTime.MinValue; HttpWebResponse httpResponse = response as HttpWebResponse; if (httpResponse != null) { lastModified = httpResponse.LastModified; } else { FtpWebResponse ftpResponse = response as FtpWebResponse; if (ftpResponse != null) { lastModified = ftpResponse.LastModified; } } GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() lastModified:" + lastModified.ToString() + " (script):" + (scriptInstance == null ? "(null)" : scriptInstance.LastModified.ToString())); if (scriptInstance != null && lastModified != DateTime.MinValue && scriptInstance.LastModified == lastModified) { newScriptInstance = scriptInstance; newState = AutoWebProxyState.Completed; } else { string scriptBody = null; byte[] scriptBuffer = null; using (Stream responseStream = response.GetResponseStream()) { SingleItemRequestCache.ReadOnlyStream ros = responseStream as SingleItemRequestCache.ReadOnlyStream; if (ros != null) { scriptBuffer = ros.Buffer; } if (scriptInstance != null && scriptBuffer != null && scriptBuffer == scriptInstance.Buffer) { scriptInstance.LastModified = lastModified; newScriptInstance = scriptInstance; newState = AutoWebProxyState.Completed; GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() Buffer matched - reusing Engine."); } else { using (StreamReader streamReader = new StreamReader(responseStream)) { scriptBody = streamReader.ReadToEnd(); } } } WebResponse tempResponse = response; response = null; tempResponse.Close(); timer.Cancel(); timer = null; if (newState != AutoWebProxyState.Completed) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() IsFromCache:" + tempResponse.IsFromCache.ToString() + " scriptInstance:" + ValidationHelper.HashString(scriptInstance)); if (scriptInstance != null && scriptBody == scriptInstance.ScriptBody) { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() Script matched - using existing Engine."); scriptInstance.LastModified = lastModified; if (scriptBuffer != null) { scriptInstance.Buffer = scriptBuffer; } newScriptInstance = scriptInstance; newState = AutoWebProxyState.Completed; } else { GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() Creating AutoWebProxyScriptWrapper."); newScriptInstance = new AutoWebProxyScriptWrapper(); newScriptInstance.LastModified = lastModified; if (newScriptInstance.Compile(location, scriptBody, scriptBuffer)) { newState = AutoWebProxyState.Completed; } else { newState = AutoWebProxyState.CompilationFailure; } } } } } catch (Exception exception) { if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_script_download_compile_error, exception)); GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() Download() threw:" + ValidationHelper.ToString(exception)); } finally { if (timer != null) { timer.Cancel(); } // try { if (response != null) { response.Close(); } } finally { WebPermission.RevertAssert(); // The request is not needed anymore. Set it to null, so if Abort() gets called, // after this point, it will result in a no-op. request = null; } } if ((newState == AutoWebProxyState.Completed) && (scriptInstance != newScriptInstance)) { if (scriptInstance != null) { scriptInstance.Close(); } scriptInstance = newScriptInstance; } GlobalLog.Print("NetWebProxyFinder#" + ValidationHelper.HashString(this) + "::DownloadAndCompile() retuning newState:" + ValidationHelper.ToString(newState)); return newState; } private static IList ParseScriptResult(string scriptReturn) { IList result = new List (); if (scriptReturn == null) { return result; } string[] proxyListStrings = scriptReturn.Split(splitChars); string proxyAuthority; foreach (string s in proxyListStrings) { string proxyString = s.Trim(' '); if (!proxyString.StartsWith("PROXY ", StringComparison.OrdinalIgnoreCase)) { if (string.Compare("DIRECT", proxyString, StringComparison.OrdinalIgnoreCase) == 0) { proxyAuthority = null; } else { continue; } } else { // remove prefix "PROXY " (6 chars) from the string and trim additional leading spaces. proxyAuthority = proxyString.Substring(6).TrimStart(' '); Uri uri = null; bool tryParse = Uri.TryCreate("http://" + proxyAuthority, UriKind.Absolute, out uri); if (!tryParse || uri.UserInfo.Length > 0 || uri.HostNameType == UriHostNameType.Basic || uri.AbsolutePath.Length != 1 || proxyAuthority[proxyAuthority.Length - 1] == '/' || proxyAuthority[proxyAuthority.Length - 1] == '#' || proxyAuthority[proxyAuthority.Length - 1] == '?') { continue; } } result.Add(proxyAuthority); } return result; } private void DetectScriptLocation() { if (scriptDetectionFailed || scriptLocation != null) { return; } GlobalLog.Print("NetWebProxyFinder::DetectScriptLocation() Attempting discovery PROXY_AUTO_DETECT_TYPE_DHCP."); scriptLocation = SafeDetectAutoProxyUrl(UnsafeNclNativeMethods.WinHttp.AutoDetectType.Dhcp); if (scriptLocation == null) { GlobalLog.Print("NetWebProxyFinder::DetectScriptLocation() Attempting discovery AUTO_DETECT_TYPE_DNS_A."); scriptLocation = SafeDetectAutoProxyUrl(UnsafeNclNativeMethods.WinHttp.AutoDetectType.DnsA); } if (scriptLocation == null) { GlobalLog.Print("NetWebProxyFinder::DetectScriptLocation() Discovery failed."); scriptDetectionFailed = true; } } // from wininet.h // // #define INTERNET_MAX_PATH_LENGTH 2048 // #define INTERNET_MAX_PROTOCOL_NAME "gopher" // longest protocol name // #define INTERNET_MAX_URL_LENGTH ((sizeof(INTERNET_MAX_PROTOCOL_NAME) - 1) \ // + sizeof("://") \ // + INTERNET_MAX_PATH_LENGTH) // private const int MaximumProxyStringLength = 2058; /// /// [SuppressMessage("Microsoft.Reliability","CA2001:AvoidCallingProblematicMethods", MessageId="System.Runtime.InteropServices.SafeHandle.DangerousGetHandle", Justification="Implementation requires DangerousGetHandle")] private static unsafe Uri SafeDetectAutoProxyUrl( UnsafeNclNativeMethods.WinHttp.AutoDetectType discoveryMethod) { Uri autoProxy = null; #if !FEATURE_PAL string url = null; if (ComNetOS.IsWinHttp51) { GlobalLog.Print("NetWebProxyFinder::SafeDetectAutoProxyUrl() Using WinHttp."); SafeGlobalFree autoProxyUrl; bool success = UnsafeNclNativeMethods.WinHttp.WinHttpDetectAutoProxyConfigUrl(discoveryMethod, out autoProxyUrl); if (!success) { if (autoProxyUrl != null) { autoProxyUrl.SetHandleAsInvalid(); } } else { url = new string((char*)autoProxyUrl.DangerousGetHandle()); autoProxyUrl.Close(); } } else { GlobalLog.Print("NetWebProxyFinder::SafeDetectAutoProxyUrl() Using WinInet."); StringBuilder autoProxyUrl = new StringBuilder(MaximumProxyStringLength); bool success = UnsafeNclNativeMethods.WinInet.DetectAutoProxyUrl( autoProxyUrl, MaximumProxyStringLength, (int)discoveryMethod); if (success) { url = autoProxyUrl.ToString(); } } if (url != null) { bool parsed = Uri.TryCreate(url, UriKind.Absolute, out autoProxy); if (!parsed) { if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_autodetect_script_location_parse_error, ValidationHelper.ToString(url))); GlobalLog.Print("NetWebProxyFinder::SafeDetectAutoProxyUrl() Uri.TryParse() failed url:" + ValidationHelper.ToString(url)); } } else { if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_autodetect_failed)); GlobalLog.Print("NetWebProxyFinder::SafeDetectAutoProxyUrl() DetectAutoProxyUrl() returned false"); } #endif // !FEATURE_PAL return autoProxy; } // RequestTimeoutCallback - Called by the TimerThread to abort a request. This just posts ThreadPool work item - Abort() does too // much to be done on the timer thread (timer thread should never block or call user code). private static void RequestTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context) { ThreadPool.UnsafeQueueUserWorkItem(abortWrapper, context); } private static void AbortWrapper(object context) { #if DEBUG GlobalLog.SetThreadSource(ThreadKinds.Worker); using (GlobalLog.SetThreadKind(ThreadKinds.System)) { #endif if (context != null) { ((WebRequest)context).Abort(); } #if DEBUG } #endif } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved./// Called to discover script location. This performs /// autodetection using the method specified in the detectFlags. /// ///
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ServiceContractViewControl.Designer.cs
- TableProviderWrapper.cs
- FileEnumerator.cs
- Span.cs
- MessageVersion.cs
- RadioButtonAutomationPeer.cs
- NoClickablePointException.cs
- AdRotator.cs
- CompoundFileReference.cs
- PartialArray.cs
- CopyAction.cs
- RepeaterCommandEventArgs.cs
- NonVisualControlAttribute.cs
- SecurityManager.cs
- FilteredAttributeCollection.cs
- NegotiateStream.cs
- EncodingNLS.cs
- DataGridToolTip.cs
- AbstractSvcMapFileLoader.cs
- ImpersonationContext.cs
- TextRenderer.cs
- HttpHostedTransportConfiguration.cs
- FileSystemInfo.cs
- DataGridViewCellStateChangedEventArgs.cs
- TypedTableBase.cs
- TableChangeProcessor.cs
- PeerNearMe.cs
- TouchPoint.cs
- KeyNotFoundException.cs
- CngAlgorithmGroup.cs
- Pair.cs
- NetTcpBindingCollectionElement.cs
- ValidatingReaderNodeData.cs
- StaticTextPointer.cs
- Win32NamedPipes.cs
- BamlBinaryReader.cs
- MetadataArtifactLoaderFile.cs
- HttpGetProtocolImporter.cs
- __Filters.cs
- InstanceHandleReference.cs
- GeometryModel3D.cs
- SecurityDocument.cs
- ChildrenQuery.cs
- XmlElementAttribute.cs
- SyntaxCheck.cs
- MultiSelector.cs
- SetStateEventArgs.cs
- DataKeyCollection.cs
- FixedSOMGroup.cs
- ObjectDisposedException.cs
- PropVariant.cs
- dataSvcMapFileLoader.cs
- XmlElement.cs
- PropertyCollection.cs
- SecurityDocument.cs
- XmlSchemaRedefine.cs
- OdbcConnection.cs
- PartialCachingControl.cs
- AccessDataSourceView.cs
- ValidationErrorEventArgs.cs
- Line.cs
- DesignerObjectListAdapter.cs
- CurrencyWrapper.cs
- SetterBase.cs
- DispatcherTimer.cs
- ConfigXmlAttribute.cs
- OperationFormatUse.cs
- SqlDataRecord.cs
- HttpBufferlessInputStream.cs
- RSAOAEPKeyExchangeDeformatter.cs
- JournalEntry.cs
- DataPagerField.cs
- DrawingImage.cs
- XmlSchemaSimpleType.cs
- CategoryAttribute.cs
- ReadOnlyActivityGlyph.cs
- DataGridLinkButton.cs
- DecimalConverter.cs
- ConditionValidator.cs
- _TLSstream.cs
- HttpCacheParams.cs
- FontDifferentiator.cs
- TableRow.cs
- RtfFormatStack.cs
- BinHexEncoder.cs
- Directory.cs
- SortableBindingList.cs
- ParallelTimeline.cs
- WebPartMinimizeVerb.cs
- SqlStatistics.cs
- ProfileService.cs
- FormatterServices.cs
- Expression.cs
- AdapterDictionary.cs
- CharEntityEncoderFallback.cs
- ComponentChangingEvent.cs
- InfoCardAsymmetricCrypto.cs
- RecordsAffectedEventArgs.cs
- ScriptDescriptor.cs
- DbDataReader.cs