Using HTTP Proxy Servers

While most users are directly connected to the Internet, or behind a firewall/NAT that transparently forwards network traffic, some sites require manual proxy configuration in order for an application to successfully make HTTP requests.

How to Test

Unless you’re “lucky” enough to work at a company that has a non-transparent proxy server between you and the Internet, you’ll need to set one up on a dedicated network for testing. Before you do that, a great way to test on your dev box is to install Fiddler, and enable the Rules > Require Proxy Authentication option. Fiddler’s default proxy address is http://127.0.0.1:8888, and the user name and password for proxy authentication are both “1”. (It can’t stop your code completely bypassing this proxy—the real test environment will have to be used to check for that—but it can show that traffic is successfully going through the proxy when expected.)

Default Proxy

If the proxy server is configured in Internet Explorer’s Tools > Internet Options > Connections > LAN settings dialog, then using it is easy: just add the following section to your app.config file, and all .NET web requests will pick up the settings:

<system.net>
    <defaultProxy useDefaultCredentials="true"/>
</system.net>

Custom or Authenticated Proxy

If the settings aren’t saved in IE, or the proxy requires credentials to be manually entered, a little more work is necessary.

// collect this information from the user

Uri proxyAddress;
string userName;
string password;

// set this before any web requests or WCF calls

WebRequest.DefaultWebProxy = new WebProxy(proxyAddress)
{
    Credentials = new NetworkCredential(userName, password),
};

This code works for both HttpWebRequest and WCF; it’s not necessary (nor, in my testing, does it work for authenticated proxies) to set the BasicHttpBinding.ProxyAddress property. You do need to ensure that WCF is using the default web proxy; this is true by default and is specified in the config file:

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="BindingName" useDefaultWebProxy="true" ... >

Authenticated Proxy with BITS

By default, BITS (the Background Intelligent Transfer Service) will use the proxy configured in Internet Explorer. If the proxy is different, or if credentials must be specified, the SetProxySettings and SetCredentials methods must be called to specify the information. I won’t give the C# definition of the IBackgroundCopyJob2 interface here; a sample definition can be found elsewhere.

Setting the proxy address itself requires one call:

enum BitsJobProxyUsage
{
    Preconfig = 0,
    NoProxy = 1,
    Override = 2,
    AutoDetect = 3,
}

// NOT bitsJob.SetProxySettings(BitsJobProxyUsage.Override, proxyAddress.AbsoluteUri, "");

bitsJob.SetProxySettings(BitsJobProxyUsage.Override, proxyAddress.Authority, "");

Edit: proxyAddress.AbsoluteUri has a trailing slash, which isn’t allowed in the format specified in the SetProxySettings documentation. Instead, use Uri.Authority, which gives the host name and port (the minimum amount of information BITS needs to use a proxy).

Setting the credentials requires another call; we first define a few types we will need. (The BG_AUTH_CREDENTIALS struct in the COM interface is more complex, containing a nested union. To simplify the C# interop definition, I flattened it into one struct.)

enum BitsAuthScheme
{
    None,
    Basic,
    Digest,
    Ntlm,
    Negotiate,
    Passport,
}

enum BitsAuthTarget
{
    None,
    Server,
    Proxy,
}

[StructLayout(LayoutKind.Sequential)]
struct BitsAuthCredentials
{
    public BitsAuthTarget Target;
    public BitsAuthScheme Scheme;

    [MarshalAs(UnmanagedType.LPWStr)]
    public string UserName;

    [MarshalAs(UnmanagedType.LPWStr)]
    public string Password;
}

BITS allows the credentials to be set separately for each proxy authentication scheme; if you don’t know the type of proxy in advance, you need to specify every possibility:

foreach (var scheme in new[]
    {
        BitsAuthScheme.Basic,
        BitsAuthScheme.Digest,
        BitsAuthScheme.Ntlm,
        BitsAuthScheme.Negotiate,
    })
{
    BitsAuthCredentials credentials = new BitsAuthCredentials
    {
        Target = BitsAuthTarget.Proxy,
        Scheme = scheme,
        UserName = userName,
        Password = password,
    };

    bitsJob.SetCredentials(ref credentials);
}

These calls can be made any time before Resume is called to start the job; BITS will use the specified proxy and credentials to transfer all files in the job.

Posted by Bradley Grainger on January 21, 2010