WinHttpWebProxyFinder.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Net / System / Net / WinHttpWebProxyFinder.cs / 1305376 / WinHttpWebProxyFinder.cs

                            using System.Text; 
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Runtime.CompilerServices; 
using System.Net.Configuration;
 
namespace System.Net 
{
    // This class uses WinHttp APIs only to find, download and execute the PAC file. 
    internal sealed class WinHttpWebProxyFinder : BaseWebProxyFinder
    {
        private SafeInternetHandle session;
 
        public WinHttpWebProxyFinder(AutoWebProxyScriptEngine engine)
            : base(engine) 
        { 
            // Don't specify a user agent and dont' specify proxy settings. This is the same behavior WinHttp
            // uses when downloading the PAC file. 
            session = UnsafeNclNativeMethods.WinHttp.WinHttpOpen(null,
                UnsafeNclNativeMethods.WinHttp.AccessType.NoProxy, null, null, 0);

            // Don't throw on error, just log the error information. This is consistent with how auto-proxy 
            // works: we never throw on error (discovery, download, execution errors).
            if (session == null || session.IsInvalid) 
            { 
                int errorCode = GetLastWin32Error();
                if (Logging.On) Logging.PrintError(Logging.Web, SR.GetString(SR.net_log_proxy_winhttp_cant_open_session, errorCode)); 
            }
            else
            {
                // The default download-timeout is 1 min. 
                // WinHTTP will use the sum of all four timeouts provided in WinHttpSetTimeouts as the
                // actual timeout. Setting a value to 0 means "infinite". 
                // Since we don't provide the ability to specify finegrained timeouts like WinHttp does, 
                // we simply apply the configured timeout to all four WinHttp timeouts.
                int timeout = SettingsSectionInternal.Section.DownloadTimeout; 

                if (!UnsafeNclNativeMethods.WinHttp.WinHttpSetTimeouts(session, timeout, timeout, timeout, timeout))
                {
                    // We weren't able to set the timeouts. Just log and continue. 
                    int errorCode = GetLastWin32Error();
                    if (Logging.On) Logging.PrintError(Logging.Web, SR.GetString(SR.net_log_proxy_winhttp_timeout_error, errorCode)); 
                } 
            }
        } 

        public override bool GetProxies(Uri destination, out IList proxyList)
        {
            proxyList = null; 

            if (session == null || session.IsInvalid) 
            { 
                return false;
            } 

            if (State == AutoWebProxyState.UnrecognizedScheme)
            {
                // If a previous call already determined that we don't support the scheme of the script 
                // location, then just return false.
                return false; 
            } 

            string proxyListString = null; 
            // Set to auto-detect failed. In case auto-detect is turned off and a script-location is available
            // we'll try downloading the script from that location.
            int errorCode = (int)UnsafeNclNativeMethods.WinHttp.ErrorCodes.AudodetectionFailed;
 
            // If auto-detect is turned on, try to execute DHCP/DNS query to get PAC file, then run the script
            if (Engine.AutomaticallyDetectSettings) 
            { 
                errorCode = GetProxies(destination, null, out proxyListString);
 
                if (errorCode == (int)UnsafeNclNativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme)
                {
                    // DHCP returned FILE or FTP scheme for the PAC file location: We should stop here
                    // since this is not an error, but a feature WinHttp doesn't currently support. The 
                    // caller may be able to handle this case by using another WebProxyFinder.
                    State = AutoWebProxyState.UnrecognizedScheme; 
                    return false; 
                }
            } 

            // If auto-detect failed or was turned off, and a config-script location is available, download
            // the script from that location and execute it.
            if ((Engine.AutomaticConfigurationScript != null) && (IsRecoverableAutoProxyError(errorCode))) 
            {
                errorCode = GetProxies(destination, Engine.AutomaticConfigurationScript, 
                    out proxyListString); 
            }
 
            State = GetStateFromErrorCode(errorCode);

            if (State == AutoWebProxyState.Completed)
            { 
                if (string.IsNullOrEmpty(proxyListString))
                { 
                    // In this case the PAC file execution returned "DIRECT", i.e. WinHttp returned 
                    // 'true' with a 'null' proxy string. This state is represented as a list
                    // containing one element with value 'null'. 
                    proxyList = new string[1] { null };
                }
                else
                { 
                    // WinHttp doesn't really clear all whitespaces. It does a pretty good job with
                    // spaces, but e.g. tabs aren't removed. Therefore make sure all whitespaces get 
                    // removed. 
                    // Note: Even though the PAC script could use space characters as separators,
                    // WinHttp will always use ';' as separator character. E.g. for the PAC result 
                    // "PROXY 192.168.0.1 PROXY 192.168.0.2" WinHttp will return "192.168.0.1;192.168.0.2".
                    // WinHttp will also remove trailing ';'.
                    proxyListString = RemoveWhitespaces(proxyListString);
                    proxyList = proxyListString.Split(';'); 
                }
                return true; 
            } 

            // We get here if something went wrong, or if neither auto-detect nor script-location 
            // were turned on.
            return false;
        }
 
        public override void Abort()
        { 
            // WinHttp doesn't support aborts. Therefore we can't do anything here. 
        }
 
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            { 
                if (session != null && !session.IsInvalid)
                { 
                    session.Close(); 
                }
            } 
        }

        private int GetProxies(Uri destination, Uri scriptLocation, out string proxyListString)
        { 
            int errorCode = 0;
            proxyListString = null; 
 
            UnsafeNclNativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions =
                new UnsafeNclNativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS(); 

            // Always try to download the PAC file without authentication. If we turn auth. on, the WinHttp
            // service will create a new session for every request (performance/memory implications).
            // Therefore we only turn auto-logon on if it is really needed. 
            autoProxyOptions.AutoLogonIfChallenged = false;
 
            if (scriptLocation == null) 
            {
                // Use auto-discovery to find the script location. 
                autoProxyOptions.Flags = UnsafeNclNativeMethods.WinHttp.AutoProxyFlags.AutoDetect;
                autoProxyOptions.AutoConfigUrl = null;
                autoProxyOptions.AutoDetectFlags = UnsafeNclNativeMethods.WinHttp.AutoDetectType.Dhcp |
                    UnsafeNclNativeMethods.WinHttp.AutoDetectType.DnsA; 
            }
            else 
            { 
                // Use the provided script location for the PAC file.
                autoProxyOptions.Flags = UnsafeNclNativeMethods.WinHttp.AutoProxyFlags.AutoProxyConfigUrl; 
                autoProxyOptions.AutoConfigUrl = scriptLocation.ToString();
                autoProxyOptions.AutoDetectFlags = UnsafeNclNativeMethods.WinHttp.AutoDetectType.None;
            }
 
            if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString))
            { 
                errorCode = GetLastWin32Error(); 

                // If the PAC file can't be downloaded because auth. was required, we check if the 
                // credentials are set; if so, then we try again using auto-logon.
                // Note that by default webProxy.Credentials will be null. The user needs to set
                //  in the config file, in order for
                // webProxy.Credentials to be set to DefaultNetworkCredentials. 
                if ((errorCode == (int)UnsafeNclNativeMethods.WinHttp.ErrorCodes.LoginFailure) &&
                    (Engine.Credentials != null)) 
                { 
                    // Now we need to try again, this time by enabling auto-logon.
                    autoProxyOptions.AutoLogonIfChallenged = true; 

                    if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions,
                        out proxyListString))
                    { 
                        errorCode = GetLastWin32Error();
                    } 
                } 

                if (Logging.On) Logging.PrintError(Logging.Web, SR.GetString(SR.net_log_proxy_winhttp_getproxy_failed, destination, errorCode)); 
            }

            return errorCode;
        } 

        private bool WinHttpGetProxyForUrl(string destination, 
            ref UnsafeNclNativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, 
            out string proxyListString)
        { 
            proxyListString = null;

            bool success = false;
            UnsafeNclNativeMethods.WinHttp.WINHTTP_PROXY_INFO proxyInfo = 
                new UnsafeNclNativeMethods.WinHttp.WINHTTP_PROXY_INFO();
 
            // Make sure the strings get cleaned up in a CER (thus unexpected exceptions, like 
            // ThreadAbortException will not interrupt the execution of the finally block, and we'll not
            // leak resources). 
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                success = UnsafeNclNativeMethods.WinHttp.WinHttpGetProxyForUrl(session, 
                    destination, ref autoProxyOptions, out proxyInfo);
 
                if (success) 
                {
                    proxyListString = Marshal.PtrToStringUni(proxyInfo.Proxy); 
                }
            }
            finally
            { 
                Marshal.FreeHGlobal(proxyInfo.Proxy);
                Marshal.FreeHGlobal(proxyInfo.ProxyBypass); 
            } 

            return success; 
        }

        private static int GetLastWin32Error()
        { 
            int errorCode = Marshal.GetLastWin32Error();
 
            if (errorCode == NativeMethods.ERROR_NOT_ENOUGH_MEMORY) 
            {
                throw new OutOfMemoryException(); 
            }

            return errorCode;
        } 

        private static bool IsRecoverableAutoProxyError(int errorCode) 
        { 
            GlobalLog.Assert(errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER,
                "WinHttpGetProxyForUrl() call: Error code 'Invalid parameter' should not be returned."); 

            // According to WinHttp the following states can be considered "recoverable", i.e.
            // we should continue trying WinHttpGetProxyForUrl() with the provided script-location
            // (if available). 
            switch ((UnsafeNclNativeMethods.WinHttp.ErrorCodes)errorCode)
            { 
                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.AutoProxyServiceError: 
                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.AudodetectionFailed:
                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.BadAutoProxyScript: 
                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.LoginFailure:
                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.OperationCancelled:
                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.Timeout:
                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.UnableToDownloadScript: 
                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme:
                    return true; 
            } 

            return false; 
        }

        private static AutoWebProxyState GetStateFromErrorCode(int errorCode)
        { 
            if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
            { 
                return AutoWebProxyState.Completed; 
            }
 
            switch ((UnsafeNclNativeMethods.WinHttp.ErrorCodes)errorCode)
            {
                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.AudodetectionFailed:
                    return AutoWebProxyState.DiscoveryFailure; 

                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.UnableToDownloadScript: 
                    return AutoWebProxyState.DownloadFailure; 

                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.BadAutoProxyScript: 
                    return AutoWebProxyState.CompilationFailure;

                case UnsafeNclNativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme:
                    return AutoWebProxyState.UnrecognizedScheme; 

                default: 
                    // We don't know the exact cause of the failure. Set the state to compilation failure to 
                    // indicate that something went wrong.
                    return AutoWebProxyState.CompilationFailure; 
            }
        }

        private static string RemoveWhitespaces(string value) 
        {
            StringBuilder result = new StringBuilder(); 
            foreach (char c in value) 
            {
                if (!char.IsWhiteSpace(c)) 
                {
                    result.Append(c);
                }
            } 

            return result.ToString(); 
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK