5.3 代理
代理可以让用户通过代理服务器浏览网络资源以达到匿名访问等目的。代理的类型有多种,常用的包括http代理和socks代理等。有时我们不希望所有的网络资源都通过代理浏览,这种情况下通常会使用pac脚本来告诉浏览器使用代理访问的规则。
Chrome浏览器提供了代理设置管理接口,这样可以让扩展来做到更加智能的代理设置。要让扩展使用代理接口,需要声明proxy
权限:
"permissions": [
"proxy"
]
通过chrome.proxy.settings.set
方法可以设置代理服务器,该方法需要两个参数,一个是代理设置对象,另一个是回调函数。
代理设置对象包括mode
属性、rules
属性和pacScript
属性。其中mode
属性为代理模式,可选的值有'direct'
(直接连接,即不通过代理)、'auto_detect'
(通过WPAD协议自动获取pac脚本)、'pac_script'
(使用指定的pac脚本)、'fixed_servers'
(固定的代理服务器)和'system'
(使用系统的设置)。
rules
属性和pacScript
属性都是可选的,rules
指定了不同的协议通过不同的代理,比如:
var config = {
mode: "fixed_servers",
rules: {
proxyForHttp: {
scheme: "socks5",
host: "1.2.3.4",
port: 1080
},
proxyForHttps: {
scheme: "socks5",
host: "1.2.3.5",
port: 1080
},
proxyForFtp: {
scheme: "http",
host: "1.2.3.6",
port: 80
}
bypassList: ["foobar.com"]
}
};
chrome.proxy.settings.set(
{value: config},
function() {
});
上面的代码定义了所有http协议的流量都使用1.2.3.4:1080这个socks5代理服务器代理浏览,所有https协议的流量都使用1.2.3.5:1080这个socks5代理服务器浏览,所有ftp协议的流量都使用1.2.3.6:80这个http代理服务器浏览,而foobar.com的流量不使用任何代理服务器,直接进行访问。rules
还提供了singleProxy
属性(任何协议都使用此代理)和fallbackProxy
属性(未匹配到的协议使用此代理)。
pacScript
指定了使用的pac脚本,可以通过url
属性指定脚本位置,也可以直接通过data
属性指定脚本内容。pacScript
还提供了mandatory
属性以让浏览器决定当pac无效时是否阻止自动切换成直接访问,此属性默认为false
,即当pac无效时浏览器直接访问。
通过chrome.proxy.settings.get
方法可以获取到浏览器当前的代理设置:
chrome.proxy.settings.get(
{},
function(config) {
console.log(config.value);
}
);
本节将不为大家提供demo,而是直接带大家分析目前比较流行的Chrome代理管理扩展,SwitchySharp有关代理设置的核心代码。
SwitchySharp的完整代码可以通过https://code.google.com/p/switchysharp获取到,其中代理设置核心的代码为assets/scripts/plugin.js,可以通过https://code.google.com/p/switchysharp/source/browse/assets/scripts/plugin.js在线查看此文件。
var ProxyPlugin = {};
ProxyPlugin.memoryPath = memoryPath;
ProxyPlugin.proxyMode = Settings.getValue('proxyMode', 'direct');
ProxyPlugin.proxyServer = Settings.getValue('proxyServer', '');
ProxyPlugin.proxyExceptions = Settings.getValue('proxyExceptions', '');
ProxyPlugin.proxyConfigUrl = Settings.getValue('proxyConfigUrl', '');
ProxyPlugin.autoPacScriptPath = Settings.getValue('autoPacScriptPath', '');
ProxyPlugin.mute = false;
SwitchySharp首先声明了一个ProxyPlugin
对象,此对象用来储存代理设置和代理设置方法。其中proxyMode
属性为代理模式,和上文中讲到的代理模式相对应,但fixed_server
模式在proxyMode
中对应的值为manual
;proxyServer
属性为代理服务器地址;proxyExceptions
属性为不使用代理设置的例外,与上文提到的bypassList
相对应;proxyConfigUrl
属性为pac脚本的URL;autoPacScriptPath
为SwitchySharp中自动切换模式下使用的pac脚本路径。
mute
属性用来记录代理是否正在设置当中,如果不是,则此属性值为false
,如果代理设置正在被更改,则此值为ture
,用来避免设置冲突。最后_proxy
属性用来获取Chrome中代理设置的方法,为了做到最大限度兼容,SwitchySharp对代理接口依然处于实验性阶段版本的Chrome进行了优化:
if (chrome.experimental !== undefined && chrome.experimental.proxy !== undefined)
ProxyPlugin._proxy = chrome.experimental.proxy;
else if (chrome.proxy !== undefined)
ProxyPlugin._proxy = chrome.proxy;
else
alert('Need proxy api support, please update your Chrome');
ProxyPlugin
的updateProxy
方法用来更新代理设置选项,这个方法在开始就先判断mute
的值是否为真,也就是判断此时代理设置是否正在被更改,如果是则退出避免设置冲突。
ProxyPlugin._parseProxy = function (str) {
if (str) {
var proxy = {scheme:'http', host:'', port:80};
var t1 = null;
var t = str.indexOf(']') + 1;
if (t > 0) {
t1 = new Array();
t1.push(proxy.host = str.substr(0, t));
if (t < str.length - 1)
t1.push(str.substr(t + 1));
}
else {
t1 = str.split(':');
proxy.host = t1[0];
}
var t2 = proxy.host.split('=');
if (t2.length > 1) {
proxy.scheme = t2[0] == 'socks' ? 'socks4' : t2[0];
proxy.host = t2[1];
}
if (t1.length > 1)
proxy.port = parseInt(t1[1]);
return proxy;
}
else
return {}
};
_parseProxy
方法用来解析声明多种代理的规则字符串,此方法将字符串转化为用于fixed_servers
模式下的rules
对象。
ProxyPlugin.setProxy = function (proxyMode, proxyString, proxyExceptions, proxyConfigUrl) {
...
switch (proxyMode) {
case 'system':
config = {mode:"system"};
break;
...
}
ProxyPlugin.mute = true;
ProxyPlugin._proxy.settings.set({'value':config}, function () {
ProxyPlugin.mute = false;
if (ProxyPlugin.setProxyCallback != undefined) {
ProxyPlugin.setProxyCallback();
ProxyPlugin.setProxyCallback = undefined;
}
});
profile = null;
config = null;
return 0;
};
最后setProxy
方法将ProxyPlugin
中与设置相关的属性重新整合成一个适用于chrome.proxy.settings.set
方法的config
对象,并调用ProxyPlugin._proxy.settings.set
方法使之生效。