Detecting Proxy Servers using the .NET Framework
I think Microsoft really missed the mark with their web service implementation in the 1.1 Framework. Using the framework it is impossible to develop a web service application which can work reliably. This is due to the fact that the .NET Framework is unable to detect dynamic web proxy settings! The use of proxy servers is a common practice among many companies. In order to work around this limitation, I have few strategies to share.
Manual Proxy Server
If this is the scenario you are trying to support, you're in luck. Below is a screen shot of how this setting looks in IE.
Thankfully this one simply requires a modification of your application configuration file. Just add a 'proxy usessystemdefault="true"' element to the 'defaultproxy' section within the 'system.net' section of your app.config file.
With this modification, your web service calls will automatically detect these proxy server settings and utilize them.
Auto Detect and Configuration Scripts
This is where the pain begins. Basically, you must make use of some unmanaged functions to detect the proxy settings as the modification to the app.config file shown above does not work. The calls you should study include:
WinHttpGetIEProxyConfigForCurrentUser: as the name implies it will retrieve the current settings as set in the IE 'LAN Settings' dialog shown above.
WinHttpGetProxyForUrl: given a Url, this call will make use of the 'Auto Detect' proxy server settings and return the correct proxy server for the given Url. A WINHTTP_AUTOPROXY_OPTIONS
structure is passed into this call and should be configured based on the current user's IE settings
Here is a simple class that will return the valid proxy servers given a Url (NOTE: Some of the code below was obtained from here):
using System;
using System.Net;
using System.Runtime.InteropServices;
using System.Collections;
namespace MyProxyHelper
{
public class ProxyHelper
{
// Constant declarations
const int WINHTTP_ACCESS_TYPE_DEFAULT_PROXY = 0;
const int WINHTTP_ACCESS_TYPE_NO_PROXY = 1;
const int WINHTTP_ACCESS_TYPE_NAMED_PROXY = 3;
const int WINHTTP_AUTOPROXY_AUTO_DETECT = 0x00000001;
const int WINHTTP_AUTOPROXY_CONFIG_URL = 0x00000002;
const int WINHTTP_AUTOPROXY_RUN_INPROCESS = 0x00010000;
const int WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY = 0x00020000;
const int WINHTTP_AUTO_DETECT_TYPE_DHCP = 0x00000001;
const int WINHTTP_AUTO_DETECT_TYPE_DNS_A = 0x00000002;
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
struct WINHTTP_AUTOPROXY_OPTIONS
{
[MarshalAs(UnmanagedType.U4)]
public int dwFlags;
[MarshalAs(UnmanagedType.U4)]
public int dwAutoDetectFlags;
public string lpszAutoConfigUrl;
public IntPtr lpvReserved;
[MarshalAs(UnmanagedType.U4)]
public int dwReserved;
public bool fAutoLoginIfChallenged;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
struct WINHTTP_PROXY_INFO
{
[MarshalAs(UnmanagedType.U4)]
public int dwAccessType;
public IntPtr pwszProxy;
public IntPtr pwszProxyBypass;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
struct WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
{
public bool fAutoDetect;
public IntPtr lpszAutoConfigUrl;
public IntPtr lpszProxy;
public IntPtr lpszProxyBypass;
}
[DllImport("winhttp.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern IntPtr WinHttpOpen(
string pwszUserAgent,
int dwAccessType,
IntPtr pwszProxyName,
IntPtr pwszProxyBypass,
int dwFlags
);
[DllImport("winhttp.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool WinHttpCloseHandle(IntPtr hInternet);
[DllImport("winhttp.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool WinHttpGetProxyForUrl(
IntPtr hSession,
string lpcwszUrl,
ref WINHTTP_AUTOPROXY_OPTIONS pAutoProxyOptions,
ref WINHTTP_PROXY_INFO pProxyInfo
);
[DllImport("winhttp.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool WinHttpGetIEProxyConfigForCurrentUser(
ref WINHTTP_CURRENT_USER_IE_PROXY_CONFIG pProxyConfig
);
public ProxyHelper()
{
}
public IWebProxy[] DetermineProxysForUrl(string targetUrl)
{
// Open a sesssion
IntPtr hSession = WinHttpOpen("ProxyHelper", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, IntPtr.Zero, IntPtr.Zero, 0);
WebProxy[] webProxys = null;
try
{
int errorCode = 0;
// First detect the current IE settings
bool autoDetect = false;
string configURL = null;
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyConfig = new WINHTTP_CURRENT_USER_IE_PROXY_CONFIG();
if(WinHttpGetIEProxyConfigForCurrentUser(ref ieProxyConfig))
{
autoDetect = ieProxyConfig.fAutoDetect;
if(ieProxyConfig.lpszAutoConfigUrl != IntPtr.Zero)
{
configURL = Marshal.PtrToStringUni(ieProxyConfig.lpszAutoConfigUrl);
Marshal.FreeHGlobal(ieProxyConfig.lpszAutoConfigUrl);
}
if(ieProxyConfig.lpszProxy != IntPtr.Zero)
Marshal.FreeHGlobal(ieProxyConfig.lpszProxy);
if(ieProxyConfig.lpszProxyBypass != IntPtr.Zero)
Marshal.FreeHGlobal(ieProxyConfig.lpszProxyBypass);
}
// Initialize the structs
WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions = new WINHTTP_AUTOPROXY_OPTIONS();
WINHTTP_PROXY_INFO proxyInfo = new WINHTTP_PROXY_INFO();
proxyInfo.pwszProxy = proxyInfo.pwszProxyBypass = IntPtr.Zero;
bool result = false;
if(autoDetect)
{
// IE settings are to autodetect
autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
autoProxyOptions.dwAutoDetectFlags = (WINHTTP_AUTO_DETECT_TYPE_DHCPWINHTTP_AUTO_DETECT_TYPE_DNS_A);
autoProxyOptions.fAutoLoginIfChallenged = true;
//Get proxy
result = WinHttpGetProxyForUrl(hSession, targetUrl, ref autoProxyOptions, ref proxyInfo);
if(!result)
errorCode = Marshal.GetLastWin32Error();
}
if(autoDetect == false (errorCode != 0 && configURL != null))
{
// Using a configured PAC file so setup structs
autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
autoProxyOptions.lpszAutoConfigUrl = configURL;
autoProxyOptions.dwAutoDetectFlags = 0;
//Get proxy
result = WinHttpGetProxyForUrl(hSession, targetUrl, ref autoProxyOptions, ref proxyInfo);
if(!result)
errorCode = Marshal.GetLastWin32Error();
else
errorCode = 0;
}
// If we have success, get the proxy info
if(result)
{
if(proxyInfo.pwszProxy != IntPtr.Zero)
{
string proxyUrl = Marshal.PtrToStringUni(proxyInfo.pwszProxy);
Marshal.FreeHGlobal(proxyInfo.pwszProxy);
// split returned proxies into array
string[] theUrls = proxyUrl.Split(';');
// use only first one, get rid of "PROXY " prefix and whitespace
webProxys = new WebProxy[theUrls.Length];
for(int i = 0; i < theUrls.Length; i++)
{
string curProxy = theUrls[i].Replace("PROXY ", "").Trim();
webProxys.SetValue(new WebProxy(curProxy), i);
}
}
if(proxyInfo.pwszProxyBypass != IntPtr.Zero)
Marshal.FreeHGlobal(proxyInfo.pwszProxyBypass);
}
}
finally
{
if(hSession != IntPtr.Zero)
WinHttpCloseHandle(hSession);
}
return webProxys;
}
}
}

1 Comments:
Great suggestion. Just what I was looking for!
Post a Comment
<< Home