5.3 代理

优质
小牛编辑
137浏览
2023-12-01

代理可以让用户通过代理服务器浏览网络资源以达到匿名访问等目的。代理的类型有多种,常用的包括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中对应的值为manualproxyServer属性为代理服务器地址;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');

ProxyPluginupdateProxy方法用来更新代理设置选项,这个方法在开始就先判断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方法使之生效。