PAC and Portability
Uniform Server 3.5-Apollo

On the previous page I covered name-based virtual hosting. It uses the local machine's Hosts file to resolve domain names. To run a portable development server you need to edit the new target PC’s Hosts file. This page looks at resolving this issue using a PAC file and a portable browser, making the server completely portable.

PAC allows you to simulate a DNS server locally, with one line per CNAME entry. Unlike the hosts file it is dynamic. PAC is a standard, uses a single file and is supported by all new browsers.

Ever had problems testing sites with a mix of relative, root relative and absolute links? Ever wanted to test cross-site interlinking? The following trio may be what you are looking for. Uniform Server, portable (Firefox or Opera) and PAC make an ideal test environment, all on a USB memory stick.

In the light of issues raised overall, Firefox portable is the best option.

Background

Testing multi-web sites is relatively easy. First create a virtual host section for each site in the Apache configuration file. Use the hosts file on your machine to resolve the web site names to IP address. That’s it; finished. Suppose you do not have access to the host file. What alternatives are there?

If you use only relative links on your site, you can use the Apache alias and re-map. However if your sites are using absolute or root-relative links, this method will fail.

So what other methods are there for resolving IP addresses? A local DNS server would do nicely, even allow you to enter MX records. That seems a bit of overkill, since all we want to do is convince our browser to pick up pages from a different server masquerading as the real server. Enter the world of proxy servers.

Top

PAC

Web browsers can be configured to use a proxy server, allowing the use of files or other resources available on a different server.

This process is automated with a Proxy Auto-Configuration (PAC) file. Sounds complicated, but not really. The PAC file is a simple text file containing a few instructions.

Just tell your browser where to find it by setting the appropriate options. Your browser reads this file when it is re-started and whenever it needs to resolve an IP address.

Any special instructions are executed before your browser attempts to resolve an IP address. This is why PAC is so powerful.

Top

PAC file

The PAC file is a JavaScript consisting of a single function. This function receives two parameters (url and host).
Parameters are automatically provided by a browser.


Your browser calls the function using the following line: ret = FindProxyForURL(url, host);

PAC File
function FindProxyForURL(url, host)

{
  ...
}

  • url

The full URL being accessed e.g.: http://wiki.uniformserver.com/index.php/Main_Page

  • host

The hostname extracted from the URL. It is the string between :// and the first : or / after that e.g.
http://wiki.uniformserver.com/index.php/Main_Page
Note 1: The port number is not included. If required, it can be extracted from the URL.

  • ret
The return value is a string describing the configuration.

Return Value:

The JavaScript function must return a single string. If the string is null, no proxies will be used. The string can contain any of the following blocks, separated by a semicolon:

DIRECT Connections should be made directly, without any proxies.
PROXY host;port The specified proxy should be used. It's this return value we are interested in
SOCKS host;port The specified SOCKS server should be used.

There are a number of predefined functions you can use. We are interested in only two: shExpMatch and dnsDomainIs.

If these do not suit your needs, you can easily find more information on the Internet. This is worth a read, and the site has more PAC info: Navigator Proxy Auto-Config File Format.

Top

Compare function

This function compares two strings. I think the only special character it accepts is the wildcard character * (matches one or more characters). It's not a limitation and probably makes it easier to use. The function has this format:

shExpMatch(str, shexp)

  • str

any string to be compared for example the URL or the hostname.

  • shexp

the expression to compare against can use the wildcard character.

The function returns true if the string matches the specified expression.

Examples:

shExpMatch("http://home.unicenter.com/site1/index.html", "*site1*") is true.
shExpMatch("http://home.unicenter.com/site2/index.html", "*site1*") is false.

The above is just an example. Replace the fixed string with a variable such as the url, for example:

(shExpMatch(url, "*site1*");

Any url passed to the above function containing the string site1 (anywhere) will produce true.

The result can be filtered using an if statement. If true do something else continue onto the next line of code.

Filter

if (shExpMatch(url,"*site1*")) return "PROXY 127.0.0.1";

When the comparison is true, the something to do is to return an IP address of a proxy server. The url our browser was looking for can be found on the proxy server 127.0.0.1 (that's our local test server). It's this return value that resolves the IP address hence the browser is happy and fetches the information.

Top

Complete PAC file

Add this filter to the PAC function and we have our very own DNS resolver, with the ability to define any CNAME that we wish.

test.pac File

function FindProxyForURL(url, host)
{
  if (shExpMatch(url,"*site1*")) return "PROXY 127.0.0.1";
  return "";
}

Note: If a match is not found the function returns a null value. This means the poor old browser needs to do a little more work to resolve the IP address. It checks the hosts file, then any local DNS server, and as a last resort puts a request onto the Internet to a DNS server.

Top

PAC and Hosts file

From the previous page we have the following entries in the Hosts file:

  • 127.0.0.1 www.my_site1.fredtest.mine.nu
  • 127.0.0.1 www.my_site2.fredtest.mine.nu
  • 127.0.0.1 www.ric.com

With a PAC file, specific strings can be targeted. We are interested in resolving domain names and not what is typed into a browser. I have highlighted in bold these domain names. There are only two domains to resolve: mine.nu and ric.com hence our PAC file requires only two lines as follows:

test.pac File
function FindProxyForURL(url, host)
{
  if (shExpMatch(url,"*.mine.nu/*")) return "PROXY 127.0.0.1";
  if (shExpMatch(url,"*.ric.com/*")) return "PROXY 127.0.0.1";
  return "";
} 

Alternative function

If you do not like using wild cards, you can use the function dnsDomainIs to directly target a domain name.

dnsDomainIs(host, domain)

  • host

is the hostname from the URL e.g http://wiki.uniformserver.com/index.php/Main_Page

  • domain

is the domain name to test the hostname against e.g. .uniformserver.com

Returns true if the domain of hostname matches.

test1.pac File
function FindProxyForURL(url, host)
{
  if (dnsDomainIs(host,".mine.nu")) return "PROXY 127.0.0.1";
  if (dnsDomainIs(host,".ric.com")) return "PROXY 127.0.0.1";
  return "";
} 

Top

Mimic Hosts file

You can mimic the host file as follows:

test2.pac File
function FindProxyForURL(url, host)
{
  if (shExpMatch(url,"http://www.my_site1.fredtest.mine.nu/*")) return "PROXY 127.0.0.1";
  if (shExpMatch(url,"http://www.my_site2.fredtest.mine.nu/*")) return "PROXY 127.0.0.1";
  if (shExpMatch(url,"http://www.ric.com/*")) return "PROXY 127.0.0.1";
  return "";
} 

Top

Feedback from the forum (27-6-07)

This was posted by figment88 and provides another interesting way to use a PAC file; you can read the full post here Uniform Forum. All I have done is cut and paste to here.

I wanted to add an extra tip on top of the UniCenter tutorial. The tutorial shows you how to setup a proxy file so you can have name based virtual hosts without modifying the current computer's HOST file. The examples in the tutorial require you to modify your proxy file (*.pac) to add a line for every new virtual host. In addition, the examples use wildcard matching, so if you name your local site "example" and the real site is "example.com" the browser might not let you load both.

I made a simple proxy file that solves both these issues

figment88.pac
function FindProxyForURL(url, host) {

if(isPlainHostName(host)) return "PROXY 127.0.0.1";

return "DIRECT";
} 

The isPlainHostName looks for hosts that have a dot. So, I always name my local version of a website as a single word; e.g,

site1 is site1.com
site2 is site2.com


Comment: My original pac file contained wildcard matching, not intentional since I especially wanted to target each site and prevent my browser looking on the Internet. I have included both versions for completeness. In contrast figment88 wants to view his development site locally and the published version hence his neat solution.

Summary

The above was just a quick introduction to the PAC file; it is much more powerful and worth further investigation. Search the Internet for more information. I have only covered what we need for our purpose. The test.pac file is about as complex as it gets.

On the next page I show how to plumb the PAC file into your browser.

Top


  Ric