Random Tech Thoughts

The title above is not random

PAC 文件及其调试

Update on 2012-04-29. 重构 PAC 文件,用数组保存所有需要用代理访问的域名,遍历数组来创建对象以加快查找。

Proxy auto-config (PAC) 文件可指定使用代理服务器的规则。比如在教育网内不能访问国外网站,我们可以通过 PAC 文件指定仅当访问某些国外网站时使用代理服务器,其他时候直接访问。

如果熟悉 PAC 文件,只对调试方法有兴趣,请跳到 调试 PAC 文件

PAC 文件用 JavaScript 编写,必须包含 FindProxyForURL(url, host) 函数。在访问某个网址时,浏览器会调用 FindProxyForURL 根据其返回值来决定该如何访问。该函数的说明如下:

  • 参数:
  • 返回值为字符串,告诉浏览器如何访问。下面是一些可用的返回值:
    • "DIRECT" 直接访问,不使用代理
    • "PROXY host:port 使用 HTTP 代理
    • "SOCKS host:port" 使用 SOCKS 代理,因为不支持 DNS 解析,不推荐
    • "SOCKS5 host:port" 使用 SOCKS5 代理
      • 注意,Safari 虽然支持 SOCKS5 代理,但是不支持在 PAC 文件中返回的 SOCKS5,只认 SOCKS
      • 一个 workaround 是返回多个代理服务器配置,把SOCKS5 放在最前面,接一个 SOCKS,这样支持 SOCKS5 返回值的浏览器可以正常使用,而 Safari 会忽略第一个 SOCKS5。例如 SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT
      • 另一个办法是把 SOCKS 代理用polipo 转成 http 代理
    • 可以返回多个代理服务器,用分号分隔,浏览器会按顺序尝试。例如 "PROXY host:port; SOCKS5 host2:port2; DIRECT"

下面是一个 PAC 文件的例子(这个文件的目的应该很清楚),完整版本见 github

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var direct = 'DIRECT';
var http_proxy = 'PROXY host:port; DIRECT';

var blocked_list = [
  "akamai.net",
  "akamaihd.net"
];

var blocked = {};
for (var i = 0; i < blocked_list.length; i += 1) {
  blocked[blocked_list[i]] = true;
}

function host2domain(host) {
  var dotpos = host.lastIndexOf(".");
  if (dotpos === -1)
    return host;
  // Find the second last dot
  dotpos = host.lastIndexOf(".", dotpos - 1);
  if (dotpos === -1)
    return host;
  return host.substring(dotpos + 1);
};

function FindProxyForURL(url, host) {
  return blocked[host2domain(host)] ? http_proxy : direct;
};

我定义了 host2domain 函数,抽取 host 中的 domain name。然后通过检查 blocked object 是否有相应的 property 来决定是否使用代理。(为了写 PAC 文件我现学了一把 JavaScript,网上看到的例子都是一连串的 if-else,太低效而且不方便维护。写个数组遍历一把也好啊。)

PAC 文件可以使用一些预先定义好的函数,几个我觉得有用的函数如下:

  • isPlainHostName(host) 判断是否是简单域名,例如 localhost 就是一个简单域名
  • dnsDomainIs(host, domain) 判断给定的 host 是否属于某个域名
  • dnsResolve(host) 做 DNS 解析,返回 host 的 ip,注意:DNS 解析可能会 block 住浏览器
  • isInNet(ip, subnet, netmask) 判断 ip 是否属于某个子网
  • myIpAddress() 返回本机的 ip (貌似不太可靠,见 wikipedia 的说明)
  • shExpMatch(str, pattern) 判断两个字符串是否匹配,pattern 中可以包含 shell 使用的通配符

还有一些日期相关的函数,详细的列表可以看 findproxyforurl.com

使用 PAC 文件指定代理

OS X 和 Windows 都支持 PAC,在代理配置里选择自动配置代理,填入 PAC 文件的 URL 即可。(本地文件用 file://路径。)要注意的是在 HTTP 服务器上部署 PAC 文件时,需把文件的 MIME 类型设置成application/x-ns-proxy-autoconfig

如果可以控制 DNS 或者 DHCP 服务器还可以使用自动发现代理,如何部署可以参考 Wikipedia Web Proxy Autodiscovery Protocol (WPAD)

调试 PAC 文件

PAC 文件执行的环境跟网页里 JavaScript 执行的环境不同,不过用 Firefox 来调试还是挺方便的。

首先自然是让 Firefox 自己配置代理而不使用系统配置,修改过 PAC 文件后别忘了 reload 来更新。(Safari, Chrome 只能使用系统代理配置,不知怎样才能刷新系统的 PAC 缓存。)

firefox proxy conf

PAC 文件里关键点上加 alert,实际执行的时候并不会弹窗,而会在 Error Console 里打印出信息。有了这个调试就方便多了。

firefox error console

Comments