├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib ├── Pac.js ├── loadScript.js ├── pac-resolver │ ├── dateRange.js │ ├── dnsDomainIs.js │ ├── dnsDomainLevels.js │ ├── dnsResolve.js │ ├── index.js │ ├── isInNet.js │ ├── isPlainHostName.js │ ├── isResolvable.js │ ├── localHostOrDomainIs.js │ ├── myIpAddress.js │ ├── shExpMatch.js │ ├── timeRange.js │ └── weekdayRange.js └── request.js ├── package.json └── test ├── index.test.js ├── scripts ├── dateRange.js ├── dnsDomainIs.js ├── dnsDomainLevels.js ├── dnsResolve.js ├── empty.pac ├── error.pac ├── isInNet.js ├── isPlainHostName.js ├── isResolvable.js ├── localHostOrDomainIs.js ├── myIpAddress.js ├── normal.pac ├── proxy.pac ├── shExpMatch.js ├── timeRange.js └── weekdayRange.js └── units ├── dateRange.test.js ├── dnsDomainIs.test.js ├── dnsDomainLevels.test.js ├── dnsResolve.test.js ├── isIntNet.test.js ├── isPlainHostName.test.js ├── isResolvable.test.js ├── localHostOrDomainIs.test.js ├── myIpAddress.test.js ├── shExpMatch.test.js ├── timeRange.test.js └── weekdayRange.test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | max_line_length = 80 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | max_line_length = 0 15 | trim_trailing_whitespace = false 16 | 17 | [COMMIT_EDITMSG] 18 | max_line_length = 0 -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "node": true 4 | }, 5 | "extends": "eslint:recommended", 6 | "rules": { 7 | "indent": [ 8 | "error", 9 | 2 10 | ], 11 | "quotes": [ 12 | "error", 13 | "single" 14 | ], 15 | "semi": [ 16 | "error", 17 | "always" 18 | ], 19 | "no-unused-vars": [ 20 | "error", 21 | { 22 | "vars": "all", 23 | "args": "none" 24 | } 25 | ], 26 | "no-empty": [ 27 | "error", 28 | { "allowEmptyCatch": true } 29 | ], 30 | "no-cond-assign": "off" 31 | } 32 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # test 40 | test 41 | src 42 | # hidden file 43 | /.* 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.12" 5 | - "iojs" 6 | - "4" 7 | - "5" 8 | - "6" 9 | - "7" 10 | 11 | install: 12 | - npm install 13 | 14 | script: 15 | - npm run cov 16 | 17 | after_script: 18 | - npm i codecov && codecov -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 IMWeb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-pac 2 | [![node version](https://img.shields.io/badge/node.js-%3E=_0.10-green.svg?style=flat-square)](http://nodejs.org/download/) 3 | [![build status](https://img.shields.io/travis/imweb/node-pac.svg?style=flat-square)](https://travis-ci.org/imweb/node-pac) 4 | [![Test coverage](https://codecov.io/gh/imweb/node-pac/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/imweb/node-pac) 5 | [![David deps](https://img.shields.io/david/imweb/node-pac.svg?style=flat-square)](https://david-dm.org/imweb/node-pac) 6 | [![License](https://img.shields.io/npm/l/node-pac.svg?style=flat-square)](https://www.npmjs.com/package/node-pac) 7 | 8 | Node版pac脚本解析器。 9 | 10 | # 安装 11 | 12 | npm install --save node-pac 13 | 14 | # 用法 15 | 16 | var Pac = require('node-pac'); 17 | var pac = new Pac('https://raw.githubusercontent.com/imweb/node-pac/master/test/scripts/normal.pac'); 18 | 19 | pac.FindProxyForURL('http://9v.cn/index.html', function(err, res) { 20 | console.log(err, res); 21 | }); 22 | 23 | # API 24 | 1. `Pac`: 构造函数,可以通过`new Pac(options)`或`Pac(options)`两种方式生成Pac对象,其中: options为pac文件内容、本地的pac文件路径、或者http[s]链接,或者为以下对象: 25 | 26 | { 27 | url: 'pac文件内容、本地的pac文件路径、或者http[s]链接', 28 | timeout: 'url为http[s]链接时才生效,设置请求的超时时间,默认5000ms', 29 | cacheTime: '缓存pac脚本的时间,在缓存时间内,node-pac不会重新拉起pac脚本' 30 | //默认为30s,超过这个时间用户再请求时会重新拉取脚本 31 | //也可以通过下面的pac.forceUpdate()强制刷新 32 | 33 | } 34 | 35 | 2. `pac.FindProxyForURL(url, cb)`或者 `pac.findProxyForURL(url, cb)`: 通过请求url异步获取该请求的代理设置,cb的参数及返回值如下 36 | 37 | function cb(err, res){ 38 | //err: 解析出错时的错误对象 39 | //res: 解析成功返回的代理设置,如:PROXY 127.0.0.1:8080; 40 | //或 SOCKS 127.0.0.1:1080; 41 | } 42 | 43 | PS: 如果解析过程出错,按实际情况给出提示,或者直接默认为`DIRECT;` 44 | 45 | 3. `pac.FindWhistleProxyForURL(url, cb)`或者 `pac.findWhistleProxyForURL(url, cb)`: 通过请求url异步获取该请求的[whistle](https://github.com/avwo/whistle)代理设置,cb的参数及返回值如下 46 | 47 | function cb(err, res){ 48 | //err: 解析出错时的错误对象 49 | //res: 解析成功返回的代理设置,如:proxy://127.0.0.1:8080 50 | //或 socks://127.0.0.1:1080 51 | } 52 | 53 | 4. `pac.update()`: 重新获取本地或远程的pac脚本并刷新pac对象内部解析脚本,如果获取失败继续使用上一个缓存的内容,一般node-pac会自动更新,无需手动调用该方法,除非程序能确切获取脚本更新的事件或确实需要强制更新才需使用该方法。 54 | 55 | 5. `pac.forceUpdate()`: 强制重新获取本地或远程的pac脚本并刷新pac对象内部解析脚本,如果获取失败调用`pac.FindProxyForURL(url, cb)`时会把错误传给cb,一般node-pac会自动更新,无需手动调用该方法,除非程序能确切获取脚本更新的事件或确实需要强制更新才需使用该方法。 56 | 57 | # License 58 | [MIT](https://github.com/imweb/node-pac/blob/master/LICENSE) -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = require('./lib/Pac'); -------------------------------------------------------------------------------- /lib/Pac.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var pac = require('./pac-resolver'); 3 | var parseUrl = require('url').parse; 4 | var loadScript = require('./loadScript'); 5 | var CACHE_TIME = 1000 * 15; 6 | var DIRECT = function(url, host, cb) {cb(null, 'DIRECT;');}; 7 | var PROXY_RE = /(PROXY|SOCKS)\s+([^;\s]+)/i; 8 | 9 | function Pac(options, dnsResolve) { 10 | if (!(this instanceof Pac)) { 11 | return new Pac(options); 12 | } 13 | if (options instanceof Buffer) { 14 | options = options.toString(); 15 | } 16 | if (typeof options == 'string') { 17 | options = { url: options }; 18 | } 19 | 20 | if (typeof dnsResolve === 'function') { 21 | this._dnsResolve = dnsResolve; 22 | } 23 | this._options = options; 24 | this._callbacks = []; 25 | var cacheTime = parseInt(options.cacheTime, 10); 26 | this._cacheTime = isNaN(cacheTime) ? CACHE_TIME : cacheTime; 27 | } 28 | 29 | var proto = Pac.prototype; 30 | 31 | proto._setup = function(force) { 32 | var self = this; 33 | if (self._pending) { 34 | return true; 35 | } 36 | clearTimeout(self._timer); 37 | self._timer = null; 38 | if (!force && self._updateTime && Date.now() - self._updateTime < self._cacheTime) { 39 | self._timer = setTimeout(self._setup.bind(self), self._cacheTime); 40 | return; 41 | } 42 | self._pending = true; 43 | loadScript(self._options, function(err, script) { 44 | if (err) { 45 | if (force === 2 || self._script == null) { 46 | self._script = null; 47 | self._FindProxyForURL = function(url, host, cb) { 48 | cb(err); 49 | }; 50 | } 51 | } else { 52 | try { 53 | script = script || ''; 54 | if (!self._FindProxyForURL || self._script !== script) { 55 | self._script = script; 56 | self._FindProxyForURL = script ? pac(script, self._dnsResolve) : DIRECT; 57 | } 58 | self._updateTime = Date.now(); 59 | } catch(e) { 60 | self._FindProxyForURL = function(url, host, cb) { 61 | cb(e); 62 | }; 63 | } 64 | } 65 | 66 | if (!self._FindProxyForURL) { 67 | self._FindProxyForURL = DIRECT; 68 | } 69 | 70 | self._callbacks.forEach(function(ctx) { 71 | self.FindProxyForURL(ctx.url, ctx.cb); 72 | }); 73 | self._callbacks = []; 74 | self._pending = false; 75 | }); 76 | return true; 77 | }; 78 | 79 | proto.FindProxyForURL = proto.findProxyForURL = function(url, cb) { 80 | if (url.indexOf('://') == -1) { 81 | url = 'http://' + url; 82 | } 83 | if (this._FindProxyForURL) { 84 | this._FindProxyForURL(url, parseUrl(url).hostname, cb); 85 | } else { 86 | this._callbacks.push({ 87 | url: url, 88 | cb: cb 89 | }); 90 | } 91 | this._setup(); 92 | }; 93 | 94 | proto.FindWhistleProxyForURL = proto.findWhistleProxyForURL = function(url, cb) { 95 | this.FindProxyForURL(url, function(err, res) { 96 | if (!err && PROXY_RE.test(res)) { 97 | var proxyUrl = RegExp.$1.toLowerCase() + '://' + RegExp.$2; 98 | var rule = RegExp['$&']; 99 | var index = res.indexOf(rule) + rule.length; 100 | var prefix = res.toLowerCase().indexOf('direct', index) !== -1 ? 'x' : ''; 101 | cb(err, prefix + proxyUrl); 102 | } else { 103 | cb(err, ''); 104 | } 105 | }); 106 | }; 107 | 108 | proto.update = function() { 109 | return this._setup(1); 110 | }; 111 | 112 | proto.forceUpdate = function() { 113 | return this._setup(2); 114 | }; 115 | 116 | module.exports = Pac; 117 | -------------------------------------------------------------------------------- /lib/loadScript.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fs = require('fs'); 3 | var request = require('./request'); 4 | var FILE_PROTOCOL_RE = /file:\/\//; 5 | var FILE_RE = process.platform == 'win32' ? /^[a-z]:[\/\\]/i : /^\/[^\/*]/; 6 | 7 | function loadScript(options, cb) { 8 | if (typeof options === 'string') { 9 | options = {url: options}; 10 | } 11 | 12 | switch(getUrlType(options.url)) { 13 | case 'HTTP': 14 | request(options, cb); 15 | break; 16 | case 'FILE': 17 | fs.readFile(options.url, {encoding: 'utf8'}, cb); 18 | break; 19 | default: 20 | cb(null, options.url); 21 | } 22 | } 23 | 24 | function getUrlType(url) { 25 | if (/^https?:\/\//.test(url)) { 26 | return 'HTTP'; 27 | } 28 | 29 | if (FILE_PROTOCOL_RE.test(url) || FILE_RE.test(url)) { 30 | return 'FILE'; 31 | } 32 | 33 | return 'TEXT'; 34 | } 35 | 36 | module.exports = loadScript; 37 | 38 | -------------------------------------------------------------------------------- /lib/pac-resolver/dateRange.js: -------------------------------------------------------------------------------- 1 | module.exports = function dateRange () { 2 | return false; 3 | }; 4 | -------------------------------------------------------------------------------- /lib/pac-resolver/dnsDomainIs.js: -------------------------------------------------------------------------------- 1 | module.exports = function dnsDomainIs (host, domain) { 2 | host = String(host); 3 | domain = String(domain); 4 | return host.substr(domain.length * -1) === domain; 5 | }; 6 | -------------------------------------------------------------------------------- /lib/pac-resolver/dnsDomainLevels.js: -------------------------------------------------------------------------------- 1 | module.exports = function dnsDomainLevels (host) { 2 | var match = String(host).match(/\./g); 3 | var levels = 0; 4 | if (match) { 5 | levels = match.length; 6 | } 7 | return levels; 8 | }; 9 | -------------------------------------------------------------------------------- /lib/pac-resolver/dnsResolve.js: -------------------------------------------------------------------------------- 1 | var net = require('net'); 2 | 3 | module.exports = function (dnsResolve) { 4 | if (typeof dnsResolve !== 'function') { 5 | dnsResolve = null; 6 | } 7 | return function (host) { 8 | if (dnsResolve && !net.isIP(host)) { 9 | host = dnsResolve(host); 10 | } 11 | return net.isIP(host) ? host : '0.0.0.0'; 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /lib/pac-resolver/index.js: -------------------------------------------------------------------------------- 1 | var vm = require('vm'); 2 | var parseUrl = require('url').parse; 3 | var VM_OPTIONS = { 4 | displayErrors: false, 5 | timeout: 100, 6 | filename: 'proxy.pac' 7 | }; 8 | var dateRange = require('./dateRange'); 9 | var dnsDomainIs = require('./dnsDomainIs'); 10 | var dnsDomainLevels = require('./dnsDomainLevels'); 11 | var getDnsResolve = require('./dnsResolve'); 12 | var isInNet = require('./isInNet'); 13 | var isPlainHostName = require('./isPlainHostName'); 14 | var isResolvable = require('./isResolvable'); 15 | var localHostOrDomainIs = require('./localHostOrDomainIs'); 16 | var myIpAddress = require('./myIpAddress'); 17 | var shExpMatch = require('./shExpMatch'); 18 | var timeRange = require('./timeRange'); 19 | var weekdayRange = require('./weekdayRange'); 20 | 21 | var CONTEXT = vm.createContext(); 22 | 23 | setInterval(function() { 24 | CONTEXT = vm.createContext(); 25 | }, 30000); 26 | 27 | var initContext = function(dnsResolve) { 28 | CONTEXT.dateRange = dateRange; 29 | CONTEXT.dnsDomainIs = dnsDomainIs; 30 | CONTEXT.dnsDomainLevels = dnsDomainLevels; 31 | CONTEXT.dnsResolve = getDnsResolve(dnsResolve); 32 | CONTEXT.isInNet = isInNet; 33 | CONTEXT.isPlainHostName = isPlainHostName; 34 | CONTEXT.isResolvable = isResolvable; 35 | CONTEXT.localHostOrDomainIs = localHostOrDomainIs; 36 | CONTEXT.myIpAddress = myIpAddress; 37 | CONTEXT.shExpMatch = shExpMatch; 38 | CONTEXT.timeRange = timeRange; 39 | CONTEXT.weekdayRange = weekdayRange; 40 | }; 41 | 42 | var getScript = function(script) { 43 | var FindProxyForURL = 'var FindProxyForURL = (function(){\n' 44 | + script + ';\nreturn FindProxyForURL;})();FindProxyForURL'; 45 | try { 46 | return new vm.Script(FindProxyForURL); 47 | } catch(e) {} 48 | }; 49 | 50 | function generateFn(script, dnsResolve) { 51 | initContext(dnsResolve); 52 | try { 53 | var fn = script && script.runInContext(CONTEXT, VM_OPTIONS); 54 | return typeof fn === 'function' ? fn : null; 55 | } catch(e) {} 56 | } 57 | 58 | var DIRECT = 'DIRECT'; 59 | var directPac = function(url, host, cb) { 60 | return DIRECT; 61 | }; 62 | var emptyDnsResolve = function(host, cb) { 63 | cb(); 64 | }; 65 | module.exports = function(script, dnsResolve) { 66 | var hasDnsResolve = /\bdnsResolve\(/.test(script); 67 | var _dnsResolve = dnsResolve; 68 | script = getScript(script); 69 | if (!hasDnsResolve || typeof dnsResolve !== 'function') { 70 | dnsResolve = emptyDnsResolve; 71 | } 72 | return function FindProxyForURL(url, host, cb) { 73 | var result; 74 | dnsResolve(host, function() { 75 | try { 76 | host = host || parseUrl(url).hostname; 77 | var _FindProxyForURL = generateFn(script, _dnsResolve) || directPac; 78 | result = _FindProxyForURL(url, host); 79 | result = typeof result == 'string' ? result : DIRECT; 80 | } catch(e) {} finally { 81 | Object.keys(CONTEXT).forEach(function(key) { 82 | delete CONTEXT[key]; 83 | }); 84 | } 85 | if (typeof cb == 'function') { 86 | cb(null, result || DIRECT); 87 | } 88 | }); 89 | return result || DIRECT; 90 | }; 91 | }; 92 | -------------------------------------------------------------------------------- /lib/pac-resolver/isInNet.js: -------------------------------------------------------------------------------- 1 | var Netmask = require('netmask').Netmask; 2 | var net = require('net'); 3 | 4 | module.exports = function isInNet (host, pattern, mask) { 5 | if (!net.isIP(host)) { 6 | return false; 7 | } 8 | try { 9 | var netmask = new Netmask(pattern, mask); 10 | return netmask.contains(host|| '127.0.0.1'); 11 | } catch(e) {} 12 | return false; 13 | }; 14 | -------------------------------------------------------------------------------- /lib/pac-resolver/isPlainHostName.js: -------------------------------------------------------------------------------- 1 | module.exports = function isPlainHostName (host) { 2 | return !(/\./.test(host)); 3 | }; 4 | -------------------------------------------------------------------------------- /lib/pac-resolver/isResolvable.js: -------------------------------------------------------------------------------- 1 | module.exports = function isResolvable (host) { 2 | return true; 3 | }; 4 | -------------------------------------------------------------------------------- /lib/pac-resolver/localHostOrDomainIs.js: -------------------------------------------------------------------------------- 1 | module.exports = function localHostOrDomainIs (host, hostdom) { 2 | var parts = String(host).split('.'); 3 | var domparts = String(hostdom).split('.'); 4 | var matches = true; 5 | 6 | for (var i = 0; i < parts.length; i++) { 7 | if (parts[i] !== domparts[i]) { 8 | matches = false; 9 | break; 10 | } 11 | } 12 | 13 | return matches; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/pac-resolver/myIpAddress.js: -------------------------------------------------------------------------------- 1 | module.exports = function myIpAddress () { 2 | return '127.0.0.1'; 3 | }; 4 | -------------------------------------------------------------------------------- /lib/pac-resolver/shExpMatch.js: -------------------------------------------------------------------------------- 1 | function toRegExp (str) { 2 | str = String(str) 3 | .replace(/\./, '\\.') 4 | .replace(/\?/g, '.') 5 | .replace(/\*/g, '(.*)'); 6 | return new RegExp('^' + str + '$'); 7 | } 8 | 9 | module.exports = function shExpMatch (str, shexp) { 10 | var re = toRegExp(shexp); 11 | return re.test(str); 12 | }; 13 | -------------------------------------------------------------------------------- /lib/pac-resolver/timeRange.js: -------------------------------------------------------------------------------- 1 | 2 | function secondsElapsedToday (hh, mm, ss) { 3 | return ((hh*3600) + (mm*60) + ss); 4 | } 5 | 6 | function getCurrentHour (gmt, currentDate) { 7 | return (gmt ? currentDate.getUTCHours() : currentDate.getHours()); 8 | } 9 | 10 | function getCurrentMinute (gmt, currentDate) { 11 | return (gmt ? currentDate.getUTCMinutes() : currentDate.getMinutes()); 12 | } 13 | 14 | function getCurrentSecond (gmt, currentDate) { 15 | return (gmt ? currentDate.getUTCSeconds() : currentDate.getSeconds()); 16 | } 17 | 18 | // start <= value <= finish 19 | function valueInRange (start, value, finish) { 20 | return (start <= value) && (value <= finish); 21 | } 22 | 23 | module.exports = function timeRange() { 24 | var args = Array.prototype.slice.call(arguments), 25 | lastArg = args.pop(), 26 | useGMTzone = (lastArg == 'GMT'), 27 | currentDate = new Date(); 28 | 29 | if (!useGMTzone) { args.push(lastArg); } 30 | 31 | var noOfArgs = args.length, 32 | result = false, 33 | numericArgs = args.map(function(n) { return parseInt(n); }); 34 | 35 | // timeRange(hour) 36 | if (noOfArgs == 1) { 37 | result = getCurrentHour(useGMTzone, currentDate) == numericArgs[0]; 38 | 39 | // timeRange(hour1, hour2) 40 | } else if (noOfArgs == 2) { 41 | var currentHour = getCurrentHour(useGMTzone, currentDate); 42 | result = (numericArgs[0] <= currentHour) && (currentHour < numericArgs[1]); 43 | 44 | // timeRange(hour1, min1, hour2, min2) 45 | } else if (noOfArgs == 4) { 46 | result = 47 | valueInRange( 48 | secondsElapsedToday(numericArgs[0], numericArgs[1], 0), 49 | secondsElapsedToday(getCurrentHour(useGMTzone, currentDate), getCurrentMinute(useGMTzone, currentDate), 0), 50 | secondsElapsedToday(numericArgs[2], numericArgs[3], 59) 51 | ); 52 | 53 | // timeRange(hour1, min1, sec1, hour2, min2, sec2) 54 | } else if (noOfArgs == 6) { 55 | result = 56 | valueInRange( 57 | secondsElapsedToday(numericArgs[0], numericArgs[1], numericArgs[2]), 58 | secondsElapsedToday( 59 | getCurrentHour(useGMTzone, currentDate), 60 | getCurrentMinute(useGMTzone, currentDate), 61 | getCurrentSecond(useGMTzone, currentDate) 62 | ), 63 | secondsElapsedToday(numericArgs[3], numericArgs[4], numericArgs[5]) 64 | ); 65 | } 66 | 67 | return result; 68 | }; 69 | -------------------------------------------------------------------------------- /lib/pac-resolver/weekdayRange.js: -------------------------------------------------------------------------------- 1 | var dayOrder = { 'SUN': 0, 'MON': 1, 'TUE': 2, 'WED': 3, 'THU': 4, 'FRI': 5, 'SAT': 6 }; 2 | 3 | function getTodaysDay (gmt) { 4 | return (gmt ? (new Date()).getUTCDay() : (new Date()).getDay()); 5 | } 6 | 7 | // start <= value <= finish 8 | function valueInRange (start, value, finish) { 9 | return (start <= value) && (value <= finish); 10 | } 11 | 12 | module.exports = function weekdayRange (wd1, wd2, gmt) { 13 | 14 | var useGMTzone = (wd2 == 'GMT' || gmt == 'GMT'), 15 | todaysDay = getTodaysDay(useGMTzone), 16 | wd1Index = dayOrder[wd1] || -1, 17 | wd2Index = dayOrder[wd2] || -1, 18 | result = false; 19 | 20 | if (wd2Index < 0) { 21 | result = (todaysDay == wd1Index); 22 | } else { 23 | if (wd1Index <= wd2Index) { 24 | result = valueInRange(wd1Index, todaysDay, wd2Index); 25 | } else { 26 | result = valueInRange(wd1Index, todaysDay, 6) || valueInRange(0, todaysDay, wd2Index); 27 | } 28 | } 29 | return result; 30 | }; 31 | -------------------------------------------------------------------------------- /lib/request.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var https = require('https'); 3 | var extend = require('util')._extend; 4 | var parseUrl = require('url').parse; 5 | 6 | var TIMEOUT = 5000; 7 | 8 | function request(options, callback) { 9 | if (typeof options.url === 'string') { 10 | options = extend(parseUrl(options.url), options); 11 | } 12 | if (!options.agent) { 13 | options.agent = false; 14 | } 15 | var done; 16 | var execCallback = function(err, body) { 17 | if (done) { 18 | return; 19 | } 20 | done = true; 21 | callback(err, body); 22 | }; 23 | var handleResponse = function(res) { 24 | var body = ''; 25 | res.setEncoding('utf8'); 26 | res.on('error', execCallback); 27 | if (res.statusCode !== 200) { 28 | return execCallback(new Error('Respose error: ' + res.statusCode)); 29 | } 30 | res.on('data', function(data) { 31 | body += data; 32 | }); 33 | res.on('end', function() { 34 | execCallback(null, body); 35 | }); 36 | }; 37 | var req; 38 | if (options.protocol === 'https:') { 39 | req = https.request(options, handleResponse); 40 | } else { 41 | options.protocol = null; 42 | req = http.request(options, handleResponse); 43 | } 44 | var timeout = options.timeout > 0 ? options.timeout : TIMEOUT; 45 | req.setTimeout(timeout, function() { 46 | req.abort(); 47 | }); 48 | req.on('error', execCallback); 49 | req.end(options.body); 50 | return req; 51 | } 52 | 53 | module.exports = request; 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-pac", 3 | "version": "0.4.0", 4 | "description": "Node版pac脚本解析器", 5 | "scripts": { 6 | "test": "mocha test/index.test.js test/units -R spec -t 5000", 7 | "lint": "eslint ./lib *.js", 8 | "lintfix": "eslint --fix ./lib *.js", 9 | "cov": "istanbul cover _mocha -- test/index.test.js test/units -R spec -t 5000" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+ssh://git@github.com/imweb/node-pac.git" 14 | }, 15 | "keywords": [ 16 | "pac", 17 | "node" 18 | ], 19 | "author": "avenwu ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/imweb/node-pac/issues" 23 | }, 24 | "homepage": "https://github.com/imweb/node-pac#node-pac", 25 | "dependencies": { 26 | "netmask": "^1.0.6" 27 | }, 28 | "engines": { 29 | "node": ">= 0.10.0" 30 | }, 31 | "devDependencies": { 32 | "chai": "^3.5.0", 33 | "eslint": "^2.8.0", 34 | "istanbul": "^0.4.5", 35 | "mocha": "^3.0.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var expect = require('chai').expect; 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var Pac = require('../index'); 6 | var filePac = new Pac(path.join(__dirname, 'scripts/normal.pac')); 7 | var fileOptionsPac = new Pac({url: path.join(__dirname, 'scripts/normal.pac'), timeout: 3000}); 8 | var fileEmptyPac = Pac(path.join(__dirname, 'scripts/empty.pac')); 9 | var textPac = new Pac(fs.readFileSync(path.join(__dirname, 'scripts/normal.pac'))); 10 | var textOptionsPac = new Pac({url: fs.readFileSync(path.join(__dirname, 'scripts/normal.pac')), timeout: 1000}); 11 | var textErrPac = Pac(fs.readFileSync(path.join(__dirname, 'scripts/error.pac'), {encoding: 'utf8'})); 12 | var textEmptyPac = new Pac(fs.readFileSync(path.join(__dirname, 'scripts/empty.pac'), {encoding: 'utf8'})); 13 | var proxyPac = new Pac({url: fs.readFileSync(path.join(__dirname, 'scripts/proxy.pac')), timeout: 1000}); 14 | 15 | 16 | describe('#FindProxyForURL(url, cb)', function() { 17 | 18 | it('file: scripts/normal.pac', function(done) { 19 | filePac.FindProxyForURL('9v.cn', function(err, res) { 20 | expect(res).to.be.equal('PROXY 0.0.0.0:80;'); 21 | done(); 22 | }); 23 | }); 24 | 25 | it('file: scripts/normal.pac', function(done) { 26 | fileOptionsPac.FindProxyForURL('9v.cn', function(err, res) { 27 | expect(res).to.be.equal('PROXY 0.0.0.0:80;'); 28 | done(); 29 | }); 30 | }); 31 | 32 | it('file: scripts/empty.pac', function(done) { 33 | fileEmptyPac.FindProxyForURL('9v.cn', function(err, res) { 34 | expect(res).to.be.equal('DIRECT;'); 35 | done(); 36 | }); 37 | }); 38 | 39 | it('text: scripts/normal.pac', function(done) { 40 | textPac.FindProxyForURL('9v.cn', function(err, res) { 41 | expect(res).to.be.equal('PROXY 0.0.0.0:80;'); 42 | done(); 43 | }); 44 | }); 45 | 46 | it('text: scripts/normal.pac', function(done) { 47 | textOptionsPac.FindProxyForURL('9v.cn', function(err, res) { 48 | expect(res).to.be.equal('PROXY 0.0.0.0:80;'); 49 | done(); 50 | }); 51 | }); 52 | 53 | it('text: scripts/empty.pac', function(done) { 54 | textEmptyPac.FindProxyForURL('9v.cn', function(err, res) { 55 | expect(res).to.be.equal('DIRECT;'); 56 | done(); 57 | }); 58 | }); 59 | }); 60 | 61 | describe('#forceUpdate()', function() { 62 | it('return true', function() { 63 | expect(filePac.forceUpdate()).to.be.equal(true); 64 | expect(filePac.forceUpdate()).to.be.equal(true); 65 | }); 66 | 67 | }); 68 | 69 | describe('#update()', function() { 70 | it('return true', function() { 71 | expect(filePac.update()).to.be.equal(true); 72 | expect(filePac.update()).to.be.equal(true); 73 | }); 74 | 75 | }); 76 | 77 | describe('#FindProxyForURL(url, cb)', function() { 78 | 79 | it('file: scripts/normal.pac', function(done) { 80 | filePac.FindWhistleProxyForURL('9v.cn', function(err, res) { 81 | expect(res).to.be.equal('proxy://0.0.0.0:80'); 82 | done(); 83 | }); 84 | }); 85 | 86 | it('file: scripts/normal.pac', function(done) { 87 | fileOptionsPac.FindWhistleProxyForURL('9v.cn', function(err, res) { 88 | expect(res).to.be.equal('proxy://0.0.0.0:80'); 89 | done(); 90 | }); 91 | }); 92 | 93 | it('file: scripts/empty.pac', function(done) { 94 | fileEmptyPac.FindWhistleProxyForURL('9v.cn', function(err, res) { 95 | expect(res).to.be.equal(''); 96 | done(); 97 | }); 98 | }); 99 | 100 | it('text: scripts/normal.pac', function(done) { 101 | textPac.FindWhistleProxyForURL('9v.cn', function(err, res) { 102 | expect(res).to.be.equal('proxy://0.0.0.0:80'); 103 | done(); 104 | }); 105 | }); 106 | 107 | it('text: scripts/normal.pac', function(done) { 108 | textOptionsPac.FindWhistleProxyForURL('9v.cn', function(err, res) { 109 | expect(res).to.be.equal('proxy://0.0.0.0:80'); 110 | done(); 111 | }); 112 | }); 113 | 114 | it('text: scripts/empty.pac', function(done) { 115 | textEmptyPac.FindWhistleProxyForURL('9v.cn', function(err, res) { 116 | expect(res).to.be.equal(''); 117 | done(); 118 | }); 119 | }); 120 | 121 | it('text: scripts/error.pac', function(done) { 122 | textErrPac.FindWhistleProxyForURL('9v.cn', function(err, res) { 123 | done(); 124 | }); 125 | }); 126 | 127 | it('text: scripts/proxy.pac', function(done) { 128 | proxyPac.FindProxyForURL('http://www.google.com/', function(err, res) { 129 | expect(res).to.be.equal('PROXY 127.0.0.1:8123'); 130 | done(); 131 | }); 132 | }); 133 | 134 | }); 135 | -------------------------------------------------------------------------------- /test/scripts/dateRange.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (!dateRange("JAN", "MAR")) { 3 | return "SOCKS daterange.com:8080"; 4 | } 5 | 6 | return "DIRECT"; 7 | } -------------------------------------------------------------------------------- /test/scripts/dnsDomainIs.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (dnsDomainIs(host, "test.com")) { 3 | return "PROXY dnsdomainis.com:8080"; 4 | } 5 | 6 | return "DIRECT"; 7 | } -------------------------------------------------------------------------------- /test/scripts/dnsDomainLevels.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (dnsDomainLevels(host) === 0) { 3 | return "PROXY dnsdomainlevels0.com:8080"; 4 | } 5 | 6 | if (dnsDomainLevels(host) === 1) { 7 | return "PROXY dnsdomainlevels1.com:8080"; 8 | } 9 | 10 | if (dnsDomainLevels(host) === 2) { 11 | return "SOCKS dnsdomainlevels2.com:8080"; 12 | } 13 | 14 | if (dnsDomainLevels(host) === 3) { 15 | return "SOCKS dnsdomainlevels3.com:8080"; 16 | } 17 | 18 | return "DIRECT"; 19 | } -------------------------------------------------------------------------------- /test/scripts/dnsResolve.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (dnsResolve(host) == '0.0.0.0') { 3 | return "SOCKS dnsresolve.com:8080"; 4 | } 5 | 6 | return "DIRECT"; 7 | } -------------------------------------------------------------------------------- /test/scripts/empty.pac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imweb/node-pac/267dda5baa05f27f45ecb5ef01d1ed943668d8b0/test/scripts/empty.pac -------------------------------------------------------------------------------- /test/scripts/error.pac: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | // blackList (url(keyword) or domain) 5 | var blackList = [ 6 | {url:'http://58.215.179.159/'}, 7 | {url:'http://bbs.hbqc.com/ad/'}, 8 | {url:'http://www.dygang.com/d/'}, 9 | {domain:'9v.cn'}, 10 | {domain:'ete.cn'}, 11 | {domain:'pj8.net'}, 12 | {domain:'myad.cn'}, 13 | {domain:'mbai.cn'}, 14 | {domain:'csad.cc'}, 15 | {domain:'tjq.com'}, 16 | {domain:'og114.cn'}, 17 | {domain:'5636.com'}, 18 | {domain:'37cs.com'}, 19 | {domain:'a3p4.com'}, 20 | {domain:'dzgg.com'}, 21 | {domain:'2345.com'}, 22 | {domain:'ads8.com'}, 23 | {domain:'pziad.com'}, 24 | {domain:'weiyi.com'}, 25 | {domain:'fbaba.net'}, 26 | {domain:'lmaaa.com'}, 27 | {domain:'114la.com'}, 28 | {domain:'ufolm.com'}, 29 | {domain:'acbbb.com'}, 30 | {domain:'cxcxx.com'}, 31 | {domain:'18app.com'}, 32 | {domain:'my5058.com'}, 33 | {domain:'3alian.net'}, 34 | {domain:'egooad.com'}, 35 | {domain:'dianru.com'}, 36 | {domain:'sharele.cn'}, 37 | {domain:'wbindex.cn'}, 38 | {domain:'union001.cn'}, 39 | {domain:'ningmob.com'}, 40 | {domain:'wandalm.com'}, 41 | {domain:'tuhaocpc.com'}, 42 | {domain:'xinbailm.com'}, 43 | {domain:'wosewang.com'}, 44 | {domain:'jifenqiang.com'}, 45 | {domain:'union.uxin.com'}, 46 | {domain:'union.2345.com'}, 47 | {domain:'yinxiangma.com'}, 48 | {domain:'58lianmeng.com'}, 49 | {domain:'feitian001.com'}, 50 | {domain:'caolianmeng.com'}, 51 | {domain:'union.sogou.com'}, 52 | {domain:'union.xunlei.com'}, 53 | {domain:'union.ijinshan.com'}, 54 | {domain:'guanggaolianmeng.net'}, 55 | {domain:'associates.amazon.cn'}, 56 | {domain:'d1rw.com'}, 57 | {domain:'kumeng.cc'}, 58 | {domain:'yiwad.com'}, 59 | {domain:'tuite8.com'}, 60 | {domain:'mkooad.com'}, 61 | {domain:'cpm360.com'}, 62 | {domain:'liaocpa.com'}, 63 | {domain:'alimama.com'}, 64 | {domain:'media.jd.com'}, 65 | {domain:'adm.baidu.com'}, 66 | {domain:'xinyoumeng.com'}, 67 | {domain:'chinalian.com'}, 68 | {domain:'1000re.com'}, 69 | {domain:'union.1000re.com'}, 70 | {domain:'qqku.com'}, 71 | {domain:'91cu.com'}, 72 | {domain:'xun.mobi'}, 73 | {domain:'5iads.cn'}, 74 | {domain:'uogo.net'}, 75 | {domain:'wowgou.cn'}, 76 | {domain:'zhcall.com'}, 77 | {domain:'ssoolm.com'}, 78 | {domain:'ad.qqku.com'}, 79 | {domain:'10010lm.com'}, 80 | {domain:'wangmeng.com'}, 81 | {domain:'1325827506.com'}, 82 | {domain:'eeeqi.cn'}, 83 | {domain:'zun.mobi'}, 84 | {domain:'adjwl.com'}, 85 | {domain:'lmjia.com'}, 86 | {domain:'ggads.com'}, 87 | {domain:'is686.com'}, 88 | {domain:'jieku.com'}, 89 | {domain:'07171.com'}, 90 | {domain:'youmi.net'}, 91 | {domain:'xunchn.com'}, 92 | {domain:'adsame.com'}, 93 | {domain:'lianmeng.la'}, 94 | {domain:'liangao.com'}, 95 | {domain:'woyao998.com'}, 96 | {domain:'union.qqx.com'}, 97 | {domain:'2112.cc'}, 98 | {domain:'wllm.hk'}, 99 | {domain:'88lm.com'}, 100 | {domain:'5tad.com'}, 101 | {domain:'domob.cn'}, 102 | {domain:'cpa001.cn'}, 103 | {domain:'addlm.com'}, 104 | {domain:'visvn.com'}, 105 | {domain:'ti888.net'}, 106 | {domain:'adurb.com'}, 107 | {domain:'1588aa.com'}, 108 | {domain:'zhchlm.com'}, 109 | {domain:'unionli.com'}, 110 | {domain:'ylunion.com'}, 111 | {domain:'adfeiwo.com'}, 112 | {domain:'17caifu.com'}, 113 | {domain:'u.cnzol.com'}, 114 | {domain:'zreading.cn'}, 115 | {domain:'tsingliu.cn'}, 116 | {domain:'u.admin5.com'}, 117 | {domain:'lm.admin5.com'}, 118 | {domain:'lianmengpk.cn'}, 119 | {domain:'doudouguo.com'}, 120 | {domain:'quqiaoqiao.com'}, 121 | {domain:'union.itjxb.com'}, 122 | {domain:'union.ichaotu.com'}, 123 | {domain:'guanggaolianmeng.net'}, 124 | {domain:'1w.cc'}, 125 | {domain:'xn--4rr70v5ouxui.com'}, 126 | {domain:'xsu.cc'}, 127 | {domain:'ad8.cc'}, 128 | {domain:'xsu.cc'}, 129 | {domain:'hjlm.hk'}, 130 | {domain:'91gg.cn'}, 131 | {domain:'70e.com'}, 132 | {domain:'u009.com'}, 133 | {domain:'a3p4.com'}, 134 | {domain:'uogo.net'}, 135 | {domain:'7794.com'}, 136 | {domain:'hxqu.com'}, 137 | {domain:'14lm.com'}, 138 | {domain:'7958.com'}, 139 | {domain:'cpv6.com'}, 140 | {domain:'youa.net'}, 141 | {domain:'ulink.cc'}, 142 | {domain:'6adj.com'}, 143 | {domain:'6612.com'}, 144 | {domain:'cpv6.com'}, 145 | {domain:'9721.net'}, 146 | {domain:'6adj.com'}, 147 | {domain:'6dad.com'}, 148 | {domain:'zyiis.com'}, 149 | {domain:'ads80.com'}, 150 | {domain:'lrswl.com'}, 151 | {domain:'567lm.com'}, 152 | {domain:'star8.net'}, 153 | {domain:'adyun.com'}, 154 | {domain:'xt918.com'}, 155 | {domain:'xf999.com'}, 156 | {domain:'999cm.com'}, 157 | {domain:'999lm.com'}, 158 | {domain:'youday.cn'}, 159 | {domain:'xf999.com'}, 160 | {domain:'998lm.com'}, 161 | {domain:'yeapoo.cn'}, 162 | {domain:'qiyou.com'}, 163 | {domain:'7shun.com'}, 164 | {domain:'wauee.com'}, 165 | {domain:'zsj18.com'}, 166 | {domain:'114lm.com'}, 167 | {domain:'yiwad.com'}, 168 | {domain:'lmphb.com'}, 169 | {domain:'bodlm.com'}, 170 | {domain:'qidou.com'}, 171 | {domain:'umeng.co'}, 172 | {domain:'umeng.com'}, 173 | {domain:'shendu.cc'}, 174 | {domain:'ytop8.com'}, 175 | {domain:'addkm.com'}, 176 | {domain:'nvwlm.com'}, 177 | {domain:'admin9.ca'}, 178 | {domain:'hzzlm.com'}, 179 | {domain:'adsue.com'}, 180 | {domain:'yigao.com'}, 181 | {domain:'cnxad.com'}, 182 | {domain:'tuigoo.com'}, 183 | {domain:'juzilm.com'}, 184 | {domain:'jidilm.com'}, 185 | {domain:'unwzlm.com'}, 186 | {domain:'ly2002.com'}, 187 | {domain:'boyulm.com'}, 188 | {domain:'yiqifa.com'}, 189 | {domain:'9523cc.com'}, 190 | {domain:'mediav.com'}, 191 | {domain:'xinray.com'}, 192 | {domain:'kuaibu.com'}, 193 | {domain:'tiandi.com'}, 194 | {domain:'ruyilm.com'}, 195 | {domain:'onetad.com'}, 196 | {domain:'heima8.com'}, 197 | {domain:'my5058.com'}, 198 | {domain:'admin6.com'}, 199 | {domain:'adchina.cc'}, 200 | {domain:'jzgglm.com'}, 201 | {domain:'gaoduan.cc'}, 202 | {domain:'naqigs.com'}, 203 | {domain:'weiboyi.com'}, 204 | {domain:'yiqiwin.com'}, 205 | {domain:'chenggao.cn'}, 206 | {domain:'dianxin.com'}, 207 | {domain:'quantui.com'}, 208 | {domain:'meidulm.com'}, 209 | {domain:'ylunion.com'}, 210 | {domain:'liangao.com'}, 211 | {domain:'linktech.cn'}, 212 | {domain:'youyilm.com'}, 213 | {domain:'ttunion.com'}, 214 | {domain:'lianmeng.la'}, 215 | {domain:'ylunion.com'}, 216 | {domain:'u.ctrip.com'}, 217 | {domain:'adsmogo.com'}, 218 | {domain:'100tone.com'}, 219 | {domain:'ziyunlm.com'}, 220 | {domain:'ffclick.com'}, 221 | {domain:'100fenlm.cn'}, 222 | {domain:'wyunion.com'}, 223 | {domain:'ad.anzhi.com'}, 224 | {domain:'weimeigg.com'}, 225 | {domain:'wifiunion.cn'}, 226 | {domain:'lingpao8.com'}, 227 | {domain:'unionbig.com'}, 228 | {domain:'yucmedia.com'}, 229 | {domain:'union001.com'}, 230 | {domain:'9lianmeng.com'}, 231 | {domain:'yingchen69.cn'}, 232 | {domain:'zs.admin5.com'}, 233 | {domain:'toufangzhe.cn'}, 234 | {domain:'clickvalue.cn'}, 235 | {domain:'chaoyuelm.com'}, 236 | {domain:'chanet.com.cn'}, 237 | {domain:'alizhizhu.com'}, 238 | {domain:'zhongxinlm.com'}, 239 | {domain:'zhongyoulm.com'}, 240 | {domain:'58chuanmei.com'}, 241 | {domain:'union.baidu.com'}, 242 | {domain:'cps.gome.com.cn'}, 243 | {domain:'talkingdata.net'}, 244 | {domain:'media.kouclo.com'}, 245 | {domain:'zhaolianmeng.com'}, 246 | {domain:'jiujiushishi.com'}, 247 | {domain:'union.dianxin.com'}, 248 | {domain:'icloud-analysis.com'}, 249 | {domain:'icloud-diagnostics.com'}, 250 | {domain:'init.icloud-analysis.com'}, 251 | {domain:'init.icloud-diagnostics.com'}, 252 | {domain:'hm.baidu.com'}, 253 | {domain:'cbjs.baidu.com'}, 254 | {domain:'cpro.baidu.com'}, 255 | {domain:'cm.pos.baidu.com'}, 256 | {domain:'lxss.sinaapp.com'}, 257 | {domain:'rlogs.youdao.com'}, 258 | {domain:'analytics.163.com'}, 259 | {domain:'doubleclick.net'}, 260 | {domain:'google-analytics.com'}, 261 | {domain:'knet.cn'}, 262 | {domain:'20ll.cn'}, 263 | {domain:'52kmh.com'}, 264 | {domain:'si9377.com'}, 265 | {domain:'cslryy.com'}, 266 | {domain:'52kmk.com'}, 267 | {domain:'news.52kmh.com'}, 268 | {domain:'ku9377.com'}, 269 | {domain:'niuxgame77.com'}, 270 | {domain:'ali37.net'}, 271 | {domain:'js.users.51.la'}, 272 | {domain:'y2z7.com'}, 273 | {domain:'w9n0.com'}, 274 | {domain:'clouddn.com'}, 275 | {domain:'s.p.qq.com'}, 276 | {domain:'gdt.qq.com'}, 277 | {domain:'hm.l.qq.com'}, 278 | {domain:'fodder.qq.com'}, 279 | {domain:'adshmct.qq.com'}, 280 | {domain:'adpro.cn'}, 281 | {domain:'duomai.com'}, 282 | {domain:'linktech.cn'}, 283 | {domain:'adt100.com'}, 284 | {domain:'lab42.com'}, 285 | {domain:'114so.cn'}, 286 | {domain:'sh.114so.cn'}, 287 | {domain:'2345078.com'}, 288 | {domain:'2345078.com'}, 289 | {domain:'9377co.com'}, 290 | {domain:'alimama.cn'}, 291 | {domain:'g.163.com'}, 292 | {domain:'g1.163.com'}, 293 | {domain:'analytics.163.com'}, 294 | {domain:'web.stat.ws.126.net'}, 295 | {domain:'kingsoft.com'}, 296 | {domain:'mlt01.com'}, 297 | {domain:'optimix.asia'}, 298 | {domain:'bznx.net'}, 299 | {domain:'ipinyou.com'}, 300 | {domain:'tanx.com'}, 301 | {domain:'miaozhen.com'}, 302 | {domain:'allyes.cn'}, 303 | {domain:'allyes.com'}, 304 | {domain:'allyes.com.cn'}, 305 | {domain:'icybercode.com'}, 306 | {domain:'gj.qq.com'}, 307 | {domain:'dratio.com'}, 308 | {domain:'duba.net'}, 309 | {domain:'ijinshan.com'}, 310 | {domain:'sp.cc'}, 311 | {domain:'p0y.cn'}, 312 | {domain:'38ra.com'}, 313 | {domain:'88rpg.net'}, 314 | {domain:'9377os.com'}, 315 | {domain:'youle55.com'}, 316 | {domain:'s22.cnzz.com'}, 317 | {domain:'surefile.org'}, 318 | {domain:'sspadsoon.com'}, 319 | {domain:'gridsumdissector.com'}, 320 | {domain:'nfdnserror14.wo.com.cn'}, 321 | {domain:'cookiemapping.wrating.com'} 322 | ]; 323 | 324 | // whiteList (root domain) for block digital domain 325 | var whiteList = [ 326 | '6.cn', 327 | '12306.cn', 328 | '189.cn', 329 | '10086.cn', 330 | '10010.cn', 331 | '10010.com', 332 | '126.com', 333 | '163.fm', 334 | '163.com', 335 | '360.cn', 336 | '360.com' 337 | ]; 338 | 339 | 340 | function FindProxyForURL(url, host){ 341 | var direct = 'DIRECT;'; 342 | 343 | var proxy = '127.0.0.1:80'; 344 | var block = '0.0.0.0:80'; 345 | 346 | if(shExpMatch(host, '10.[0-9]+.[0-9]+.[0-9]+')) return direct; 347 | if(shExpMatch(host, '172.[0-9]+.[0-9]+.[0-9]+')) return direct; 348 | if(shExpMatch(host, '192.168.[0-9]+.[0-9]+')) return direct; 349 | if(shExpMatch(host, '127.0.0.1')) return direct; 350 | if(shExpMatch(host, 'localhost')) return direct; 351 | if(shExpMatch(host, 'dl.google.com')) return direct; 352 | 353 | if(config.blackList){ 354 | // block by blackList 355 | for(i in blackList){ 356 | if(blackList[i].url){ 357 | // block by url(keyword) 358 | if(url.indexOf(blackList[i].url) > -1){ 359 | return 'PROXY '+ block +';'; 360 | }; 361 | }; 362 | if(blackList[i].domain){ 363 | // block by domain 364 | if(shExpMatch(host, blackList[i].domain)){ 365 | return 'PROXY '+ block +';'; 366 | }; 367 | }; 368 | }; 369 | }; 370 | 371 | if(config.digitalDomain){ 372 | // block by digital domain && bypass whiteList 373 | var reg = /^([^.]+\.)*(\d+)(\.[^.]+)$/gi; 374 | if(reg.exec(host) && whiteList.indexOf(RegExp.$2 + RegExp.$3) < 0){ 375 | return 'PROXY '+ block +';'; 376 | }; 377 | }; 378 | 379 | if(config.hosts){ 380 | // proxy by hostsList 381 | for(i in hostsList){ 382 | if(dnsDomainIs(host, hostsList[i].domain)){ 383 | return 'PROXY '+ (hostsList[i].ip ? hostsList[i].ip : proxy) +';'; 384 | }; 385 | }; 386 | }; 387 | 388 | return direct; 389 | } 390 | -------------------------------------------------------------------------------- /test/scripts/isInNet.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (!isInNet(dnsResolve(host), '192.168.0.0', '255.255.255.0')) { 3 | return "SOCKS isinnet.com:8080"; 4 | } 5 | return "DIRECT"; 6 | } -------------------------------------------------------------------------------- /test/scripts/isPlainHostName.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (isPlainHostName(host)) { 3 | return "SOCKS isplainhostname.com:8080"; 4 | } 5 | 6 | return "PROXY isplainhostname.com:8080"; 7 | } -------------------------------------------------------------------------------- /test/scripts/isResolvable.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (isResolvable(host)) { 3 | return "SOCKS isresolvable.com:8080"; 4 | } 5 | 6 | return "DIRECT"; 7 | } -------------------------------------------------------------------------------- /test/scripts/localHostOrDomainIs.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (dnsDomainIs(host, "www.test.com")) { 3 | return "PROXY localhostordomainis.com:8080"; 4 | } 5 | 6 | return "DIRECT"; 7 | } -------------------------------------------------------------------------------- /test/scripts/myIpAddress.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (myIpAddress(host) === '127.0.0.1') { 3 | return "SOCKS myipaddress.com:8080"; 4 | } 5 | 6 | return "DIRECT"; 7 | } -------------------------------------------------------------------------------- /test/scripts/normal.pac: -------------------------------------------------------------------------------- 1 | /** 2 | * hosts.pac 3 | * @authors Jack.Chan 4 | * @date 2015-09-23 14:58:50 5 | * @update 2015-09-24 13:50:26 6 | */ 7 | 8 | // config (1: enabled | 0: disabled) 9 | var config = { 10 | hosts: 1, 11 | blackList: 1, 12 | digitalDomain: 0 // block digitalDomain 13 | }; 14 | 15 | 16 | // hosts 17 | var hostsList = [ 18 | {domain:'localhost', ip:'127.0.0.1:80'}, 19 | {domain:'localhost.dev'} 20 | ]; 21 | 22 | // blackList (url(keyword) or domain) 23 | var blackList = [ 24 | {url:'http://58.215.179.159/'}, 25 | {url:'http://bbs.hbqc.com/ad/'}, 26 | {url:'http://www.dygang.com/d/'}, 27 | {domain:'9v.cn'}, 28 | {domain:'ete.cn'}, 29 | {domain:'pj8.net'}, 30 | {domain:'myad.cn'}, 31 | {domain:'mbai.cn'}, 32 | {domain:'csad.cc'}, 33 | {domain:'tjq.com'}, 34 | {domain:'og114.cn'}, 35 | {domain:'5636.com'}, 36 | {domain:'37cs.com'}, 37 | {domain:'a3p4.com'}, 38 | {domain:'dzgg.com'}, 39 | {domain:'2345.com'}, 40 | {domain:'ads8.com'}, 41 | {domain:'pziad.com'}, 42 | {domain:'weiyi.com'}, 43 | {domain:'fbaba.net'}, 44 | {domain:'lmaaa.com'}, 45 | {domain:'114la.com'}, 46 | {domain:'ufolm.com'}, 47 | {domain:'acbbb.com'}, 48 | {domain:'cxcxx.com'}, 49 | {domain:'18app.com'}, 50 | {domain:'my5058.com'}, 51 | {domain:'3alian.net'}, 52 | {domain:'egooad.com'}, 53 | {domain:'dianru.com'}, 54 | {domain:'sharele.cn'}, 55 | {domain:'wbindex.cn'}, 56 | {domain:'union001.cn'}, 57 | {domain:'ningmob.com'}, 58 | {domain:'wandalm.com'}, 59 | {domain:'tuhaocpc.com'}, 60 | {domain:'xinbailm.com'}, 61 | {domain:'wosewang.com'}, 62 | {domain:'jifenqiang.com'}, 63 | {domain:'union.uxin.com'}, 64 | {domain:'union.2345.com'}, 65 | {domain:'yinxiangma.com'}, 66 | {domain:'58lianmeng.com'}, 67 | {domain:'feitian001.com'}, 68 | {domain:'caolianmeng.com'}, 69 | {domain:'union.sogou.com'}, 70 | {domain:'union.xunlei.com'}, 71 | {domain:'union.ijinshan.com'}, 72 | {domain:'guanggaolianmeng.net'}, 73 | {domain:'associates.amazon.cn'}, 74 | {domain:'d1rw.com'}, 75 | {domain:'kumeng.cc'}, 76 | {domain:'yiwad.com'}, 77 | {domain:'tuite8.com'}, 78 | {domain:'mkooad.com'}, 79 | {domain:'cpm360.com'}, 80 | {domain:'liaocpa.com'}, 81 | {domain:'alimama.com'}, 82 | {domain:'media.jd.com'}, 83 | {domain:'adm.baidu.com'}, 84 | {domain:'xinyoumeng.com'}, 85 | {domain:'chinalian.com'}, 86 | {domain:'1000re.com'}, 87 | {domain:'union.1000re.com'}, 88 | {domain:'qqku.com'}, 89 | {domain:'91cu.com'}, 90 | {domain:'xun.mobi'}, 91 | {domain:'5iads.cn'}, 92 | {domain:'uogo.net'}, 93 | {domain:'wowgou.cn'}, 94 | {domain:'zhcall.com'}, 95 | {domain:'ssoolm.com'}, 96 | {domain:'ad.qqku.com'}, 97 | {domain:'10010lm.com'}, 98 | {domain:'wangmeng.com'}, 99 | {domain:'1325827506.com'}, 100 | {domain:'eeeqi.cn'}, 101 | {domain:'zun.mobi'}, 102 | {domain:'adjwl.com'}, 103 | {domain:'lmjia.com'}, 104 | {domain:'ggads.com'}, 105 | {domain:'is686.com'}, 106 | {domain:'jieku.com'}, 107 | {domain:'07171.com'}, 108 | {domain:'youmi.net'}, 109 | {domain:'xunchn.com'}, 110 | {domain:'adsame.com'}, 111 | {domain:'lianmeng.la'}, 112 | {domain:'liangao.com'}, 113 | {domain:'woyao998.com'}, 114 | {domain:'union.qqx.com'}, 115 | {domain:'2112.cc'}, 116 | {domain:'wllm.hk'}, 117 | {domain:'88lm.com'}, 118 | {domain:'5tad.com'}, 119 | {domain:'domob.cn'}, 120 | {domain:'cpa001.cn'}, 121 | {domain:'addlm.com'}, 122 | {domain:'visvn.com'}, 123 | {domain:'ti888.net'}, 124 | {domain:'adurb.com'}, 125 | {domain:'1588aa.com'}, 126 | {domain:'zhchlm.com'}, 127 | {domain:'unionli.com'}, 128 | {domain:'ylunion.com'}, 129 | {domain:'adfeiwo.com'}, 130 | {domain:'17caifu.com'}, 131 | {domain:'u.cnzol.com'}, 132 | {domain:'zreading.cn'}, 133 | {domain:'tsingliu.cn'}, 134 | {domain:'u.admin5.com'}, 135 | {domain:'lm.admin5.com'}, 136 | {domain:'lianmengpk.cn'}, 137 | {domain:'doudouguo.com'}, 138 | {domain:'quqiaoqiao.com'}, 139 | {domain:'union.itjxb.com'}, 140 | {domain:'union.ichaotu.com'}, 141 | {domain:'guanggaolianmeng.net'}, 142 | {domain:'1w.cc'}, 143 | {domain:'xn--4rr70v5ouxui.com'}, 144 | {domain:'xsu.cc'}, 145 | {domain:'ad8.cc'}, 146 | {domain:'xsu.cc'}, 147 | {domain:'hjlm.hk'}, 148 | {domain:'91gg.cn'}, 149 | {domain:'70e.com'}, 150 | {domain:'u009.com'}, 151 | {domain:'a3p4.com'}, 152 | {domain:'uogo.net'}, 153 | {domain:'7794.com'}, 154 | {domain:'hxqu.com'}, 155 | {domain:'14lm.com'}, 156 | {domain:'7958.com'}, 157 | {domain:'cpv6.com'}, 158 | {domain:'youa.net'}, 159 | {domain:'ulink.cc'}, 160 | {domain:'6adj.com'}, 161 | {domain:'6612.com'}, 162 | {domain:'cpv6.com'}, 163 | {domain:'9721.net'}, 164 | {domain:'6adj.com'}, 165 | {domain:'6dad.com'}, 166 | {domain:'zyiis.com'}, 167 | {domain:'ads80.com'}, 168 | {domain:'lrswl.com'}, 169 | {domain:'567lm.com'}, 170 | {domain:'star8.net'}, 171 | {domain:'adyun.com'}, 172 | {domain:'xt918.com'}, 173 | {domain:'xf999.com'}, 174 | {domain:'999cm.com'}, 175 | {domain:'999lm.com'}, 176 | {domain:'youday.cn'}, 177 | {domain:'xf999.com'}, 178 | {domain:'998lm.com'}, 179 | {domain:'yeapoo.cn'}, 180 | {domain:'qiyou.com'}, 181 | {domain:'7shun.com'}, 182 | {domain:'wauee.com'}, 183 | {domain:'zsj18.com'}, 184 | {domain:'114lm.com'}, 185 | {domain:'yiwad.com'}, 186 | {domain:'lmphb.com'}, 187 | {domain:'bodlm.com'}, 188 | {domain:'qidou.com'}, 189 | {domain:'umeng.co'}, 190 | {domain:'umeng.com'}, 191 | {domain:'shendu.cc'}, 192 | {domain:'ytop8.com'}, 193 | {domain:'addkm.com'}, 194 | {domain:'nvwlm.com'}, 195 | {domain:'admin9.ca'}, 196 | {domain:'hzzlm.com'}, 197 | {domain:'adsue.com'}, 198 | {domain:'yigao.com'}, 199 | {domain:'cnxad.com'}, 200 | {domain:'tuigoo.com'}, 201 | {domain:'juzilm.com'}, 202 | {domain:'jidilm.com'}, 203 | {domain:'unwzlm.com'}, 204 | {domain:'ly2002.com'}, 205 | {domain:'boyulm.com'}, 206 | {domain:'yiqifa.com'}, 207 | {domain:'9523cc.com'}, 208 | {domain:'mediav.com'}, 209 | {domain:'xinray.com'}, 210 | {domain:'kuaibu.com'}, 211 | {domain:'tiandi.com'}, 212 | {domain:'ruyilm.com'}, 213 | {domain:'onetad.com'}, 214 | {domain:'heima8.com'}, 215 | {domain:'my5058.com'}, 216 | {domain:'admin6.com'}, 217 | {domain:'adchina.cc'}, 218 | {domain:'jzgglm.com'}, 219 | {domain:'gaoduan.cc'}, 220 | {domain:'naqigs.com'}, 221 | {domain:'weiboyi.com'}, 222 | {domain:'yiqiwin.com'}, 223 | {domain:'chenggao.cn'}, 224 | {domain:'dianxin.com'}, 225 | {domain:'quantui.com'}, 226 | {domain:'meidulm.com'}, 227 | {domain:'ylunion.com'}, 228 | {domain:'liangao.com'}, 229 | {domain:'linktech.cn'}, 230 | {domain:'youyilm.com'}, 231 | {domain:'ttunion.com'}, 232 | {domain:'lianmeng.la'}, 233 | {domain:'ylunion.com'}, 234 | {domain:'u.ctrip.com'}, 235 | {domain:'adsmogo.com'}, 236 | {domain:'100tone.com'}, 237 | {domain:'ziyunlm.com'}, 238 | {domain:'ffclick.com'}, 239 | {domain:'100fenlm.cn'}, 240 | {domain:'wyunion.com'}, 241 | {domain:'ad.anzhi.com'}, 242 | {domain:'weimeigg.com'}, 243 | {domain:'wifiunion.cn'}, 244 | {domain:'lingpao8.com'}, 245 | {domain:'unionbig.com'}, 246 | {domain:'yucmedia.com'}, 247 | {domain:'union001.com'}, 248 | {domain:'9lianmeng.com'}, 249 | {domain:'yingchen69.cn'}, 250 | {domain:'zs.admin5.com'}, 251 | {domain:'toufangzhe.cn'}, 252 | {domain:'clickvalue.cn'}, 253 | {domain:'chaoyuelm.com'}, 254 | {domain:'chanet.com.cn'}, 255 | {domain:'alizhizhu.com'}, 256 | {domain:'zhongxinlm.com'}, 257 | {domain:'zhongyoulm.com'}, 258 | {domain:'58chuanmei.com'}, 259 | {domain:'union.baidu.com'}, 260 | {domain:'cps.gome.com.cn'}, 261 | {domain:'talkingdata.net'}, 262 | {domain:'media.kouclo.com'}, 263 | {domain:'zhaolianmeng.com'}, 264 | {domain:'jiujiushishi.com'}, 265 | {domain:'union.dianxin.com'}, 266 | {domain:'icloud-analysis.com'}, 267 | {domain:'icloud-diagnostics.com'}, 268 | {domain:'init.icloud-analysis.com'}, 269 | {domain:'init.icloud-diagnostics.com'}, 270 | {domain:'hm.baidu.com'}, 271 | {domain:'cbjs.baidu.com'}, 272 | {domain:'cpro.baidu.com'}, 273 | {domain:'cm.pos.baidu.com'}, 274 | {domain:'lxss.sinaapp.com'}, 275 | {domain:'rlogs.youdao.com'}, 276 | {domain:'analytics.163.com'}, 277 | {domain:'doubleclick.net'}, 278 | {domain:'google-analytics.com'}, 279 | {domain:'knet.cn'}, 280 | {domain:'20ll.cn'}, 281 | {domain:'52kmh.com'}, 282 | {domain:'si9377.com'}, 283 | {domain:'cslryy.com'}, 284 | {domain:'52kmk.com'}, 285 | {domain:'news.52kmh.com'}, 286 | {domain:'ku9377.com'}, 287 | {domain:'niuxgame77.com'}, 288 | {domain:'ali37.net'}, 289 | {domain:'js.users.51.la'}, 290 | {domain:'y2z7.com'}, 291 | {domain:'w9n0.com'}, 292 | {domain:'clouddn.com'}, 293 | {domain:'s.p.qq.com'}, 294 | {domain:'gdt.qq.com'}, 295 | {domain:'hm.l.qq.com'}, 296 | {domain:'fodder.qq.com'}, 297 | {domain:'adshmct.qq.com'}, 298 | {domain:'adpro.cn'}, 299 | {domain:'duomai.com'}, 300 | {domain:'linktech.cn'}, 301 | {domain:'adt100.com'}, 302 | {domain:'lab42.com'}, 303 | {domain:'114so.cn'}, 304 | {domain:'sh.114so.cn'}, 305 | {domain:'2345078.com'}, 306 | {domain:'2345078.com'}, 307 | {domain:'9377co.com'}, 308 | {domain:'alimama.cn'}, 309 | {domain:'g.163.com'}, 310 | {domain:'g1.163.com'}, 311 | {domain:'analytics.163.com'}, 312 | {domain:'web.stat.ws.126.net'}, 313 | {domain:'kingsoft.com'}, 314 | {domain:'mlt01.com'}, 315 | {domain:'optimix.asia'}, 316 | {domain:'bznx.net'}, 317 | {domain:'ipinyou.com'}, 318 | {domain:'tanx.com'}, 319 | {domain:'miaozhen.com'}, 320 | {domain:'allyes.cn'}, 321 | {domain:'allyes.com'}, 322 | {domain:'allyes.com.cn'}, 323 | {domain:'icybercode.com'}, 324 | {domain:'gj.qq.com'}, 325 | {domain:'dratio.com'}, 326 | {domain:'duba.net'}, 327 | {domain:'ijinshan.com'}, 328 | {domain:'sp.cc'}, 329 | {domain:'p0y.cn'}, 330 | {domain:'38ra.com'}, 331 | {domain:'88rpg.net'}, 332 | {domain:'9377os.com'}, 333 | {domain:'youle55.com'}, 334 | {domain:'s22.cnzz.com'}, 335 | {domain:'surefile.org'}, 336 | {domain:'sspadsoon.com'}, 337 | {domain:'gridsumdissector.com'}, 338 | {domain:'nfdnserror14.wo.com.cn'}, 339 | {domain:'cookiemapping.wrating.com'} 340 | ]; 341 | 342 | // whiteList (root domain) for block digital domain 343 | var whiteList = [ 344 | '6.cn', 345 | '12306.cn', 346 | '189.cn', 347 | '10086.cn', 348 | '10010.cn', 349 | '10010.com', 350 | '126.com', 351 | '163.fm', 352 | '163.com', 353 | '360.cn', 354 | '360.com' 355 | ]; 356 | 357 | 358 | function FindProxyForURL(url, host){ 359 | var direct = 'DIRECT;'; 360 | 361 | var proxy = '127.0.0.1:80'; 362 | var block = '0.0.0.0:80'; 363 | 364 | if(shExpMatch(host, '10.[0-9]+.[0-9]+.[0-9]+')) return direct; 365 | if(shExpMatch(host, '172.[0-9]+.[0-9]+.[0-9]+')) return direct; 366 | if(shExpMatch(host, '192.168.[0-9]+.[0-9]+')) return direct; 367 | if(shExpMatch(host, '127.0.0.1')) return direct; 368 | if(shExpMatch(host, 'localhost')) return direct; 369 | if(shExpMatch(host, 'dl.google.com')) return direct; 370 | 371 | if(config.blackList){ 372 | // block by blackList 373 | for(i in blackList){ 374 | if(blackList[i].url){ 375 | // block by url(keyword) 376 | if(url.indexOf(blackList[i].url) > -1){ 377 | return 'PROXY '+ block +';'; 378 | }; 379 | }; 380 | if(blackList[i].domain){ 381 | // block by domain 382 | if(shExpMatch(host, blackList[i].domain)){ 383 | return 'PROXY '+ block +';'; 384 | }; 385 | }; 386 | }; 387 | }; 388 | 389 | if(config.digitalDomain){ 390 | // block by digital domain && bypass whiteList 391 | var reg = /^([^.]+\.)*(\d+)(\.[^.]+)$/gi; 392 | if(reg.exec(host) && whiteList.indexOf(RegExp.$2 + RegExp.$3) < 0){ 393 | return 'PROXY '+ block +';'; 394 | }; 395 | }; 396 | 397 | if(config.hosts){ 398 | // proxy by hostsList 399 | for(i in hostsList){ 400 | if(dnsDomainIs(host, hostsList[i].domain)){ 401 | return 'PROXY '+ (hostsList[i].ip ? hostsList[i].ip : proxy) +';'; 402 | }; 403 | }; 404 | }; 405 | 406 | return direct; 407 | } 408 | -------------------------------------------------------------------------------- /test/scripts/proxy.pac: -------------------------------------------------------------------------------- 1 | var FindProxyForURL = function FindProxyForURL(init, profiles) { 2 | return function (url, host) { 3 | "use strict"; 4 | var result = init, scheme = url.substr(0, url.indexOf(":")); 5 | do { 6 | result = profiles[result]; 7 | if (typeof result === "function") 8 | result = result(url, host, scheme); 9 | } while (typeof result !== "string" || result.charCodeAt(0) === 43); 10 | return result; 11 | }; 12 | }("+google", { 13 | "+google" : function (url, host, scheme) { 14 | "use strict"; 15 | if (/(?:^|\.)google\.com$/.test(host)) 16 | return "+internal-vf"; 17 | return "DIRECT"; 18 | }, 19 | "+internal-vf" : function (url, host, scheme) { 20 | "use strict"; 21 | if (host === "127.0.0.1" || host === "::1" || host.indexOf(".") < 0) 22 | return "DIRECT"; 23 | return "PROXY 127.0.0.1:8123"; 24 | } 25 | }); -------------------------------------------------------------------------------- /test/scripts/shExpMatch.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (shExpMatch(host, "*.test.com")) { 3 | return "PROXY shexpmatch1.com:8080"; 4 | } 5 | 6 | if (shExpMatch(host, "?.test2.com")) { 7 | return "PROXY shexpmatch2.com:8080"; 8 | } 9 | 10 | return "DIRECT"; 11 | } -------------------------------------------------------------------------------- /test/scripts/timeRange.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | var hours = new Date().getHours(); 3 | var start, end; 4 | if (hours > 0) { 5 | start = hours - 1; 6 | end = hours + 1; 7 | } else { 8 | start = hours; 9 | end = hours + 1; 10 | } 11 | if (timeRange(start, end)) { 12 | return "PROXY timerange.com:8080"; 13 | } 14 | 15 | return "DIRECT"; 16 | } -------------------------------------------------------------------------------- /test/scripts/weekdayRange.js: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if (weekdayRange("SUN", "SAT")) { 3 | return "SOCKS weekdayrange.com:8080"; 4 | } 5 | 6 | return "DIRECT"; 7 | } -------------------------------------------------------------------------------- /test/units/dateRange.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/dateRange.js'), timeout: 1000}); 6 | 7 | describe('#dateRange', function() { 8 | it('in the range', function(done) { 9 | testPac.FindWhistleProxyForURL('www.ifeng.com', function(err, res) { 10 | expect(res === 'socks://daterange.com:8080').to.be.equal(true); 11 | done(); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/units/dnsDomainIs.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/dnsDomainIs.js'), timeout: 1000}); 6 | 7 | describe('#dnsDomainIs', function() { 8 | it('match', function(done) { 9 | testPac.FindWhistleProxyForURL('www.test.com', function(err, res) { 10 | expect(res === 'proxy://dnsdomainis.com:8080').to.be.equal(true); 11 | done(); 12 | }); 13 | }); 14 | 15 | it('mismatch', function(done) { 16 | testPac.FindWhistleProxyForURL('www.ifeng.com', function(err, res) { 17 | expect(res === '').to.be.equal(true); 18 | done(); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/units/dnsDomainLevels.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/dnsDomainLevels.js'), timeout: 1000}); 6 | 7 | describe('#dnsDomainLevels', function() { 8 | it('levels === 0', function(done) { 9 | testPac.FindWhistleProxyForURL('http://www', function(err, res) { 10 | expect(res === 'proxy://dnsdomainlevels0.com:8080').to.be.equal(true); 11 | done(); 12 | }); 13 | }); 14 | it('levels === 1', function(done) { 15 | testPac.FindWhistleProxyForURL('http://www.test', function(err, res) { 16 | expect(res === 'proxy://dnsdomainlevels1.com:8080').to.be.equal(true); 17 | done(); 18 | }); 19 | }); 20 | it('levels === 2', function(done) { 21 | testPac.FindWhistleProxyForURL('http://www.test.com', function(err, res) { 22 | expect(res === 'socks://dnsdomainlevels2.com:8080').to.be.equal(true); 23 | done(); 24 | }); 25 | }); 26 | it('levels === 3', function(done) { 27 | testPac.FindWhistleProxyForURL('http://www.test.com.cn', function(err, res) { 28 | expect(res === 'socks://dnsdomainlevels3.com:8080').to.be.equal(true); 29 | done(); 30 | }); 31 | }); 32 | it('levels > 3', function(done) { 33 | testPac.FindWhistleProxyForURL('www.test.com.cn.test', function(err, res) { 34 | expect(res === '').to.be.equal(true); 35 | done(); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/units/dnsResolve.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/dnsResolve.js'), timeout: 1000}); 6 | 7 | describe('#dnsResolve', function() { 8 | it('unresolvable', function(done) { 9 | testPac.FindWhistleProxyForURL('www.ifeng.com', function(err, res) { 10 | expect(res === 'socks://dnsresolve.com:8080').to.be.equal(true); 11 | done(); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/units/isIntNet.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/isInNet.js'), timeout: 1000}); 6 | 7 | describe('#isInNet', function() { 8 | it('not in the range', function(done) { 9 | testPac.FindWhistleProxyForURL('www.ifeng.com', function(err, res) { 10 | expect(res === 'socks://isinnet.com:8080').to.be.equal(true); 11 | done(); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/units/isPlainHostName.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/isPlainHostName.js'), timeout: 1000}); 6 | 7 | describe('#isPlainHostName', function() { 8 | it('match', function(done) { 9 | testPac.FindWhistleProxyForURL('test', function(err, res) { 10 | expect(res === 'socks://isplainhostname.com:8080').to.be.equal(true); 11 | done(); 12 | }); 13 | }); 14 | 15 | it('mismatch', function(done) { 16 | testPac.FindWhistleProxyForURL('www.ifeng.com', function(err, res) { 17 | expect(res === 'proxy://isplainhostname.com:8080').to.be.equal(true); 18 | done(); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/units/isResolvable.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/isResolvable.js'), timeout: 1000}); 6 | 7 | describe('#isResolvable', function() { 8 | it('not in the range', function(done) { 9 | testPac.FindWhistleProxyForURL('www.ifeng.com', function(err, res) { 10 | expect(res === 'socks://isresolvable.com:8080').to.be.equal(true); 11 | done(); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/units/localHostOrDomainIs.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/localHostOrDomainIs.js'), timeout: 1000}); 6 | 7 | describe('#localHostOrDomainIs', function() { 8 | it('match', function(done) { 9 | testPac.FindWhistleProxyForURL('www.test.com', function(err, res) { 10 | expect(res === 'proxy://localhostordomainis.com:8080').to.be.equal(true); 11 | done(); 12 | }); 13 | }); 14 | 15 | it('mismatch', function(done) { 16 | testPac.FindWhistleProxyForURL('www.ifeng.com', function(err, res) { 17 | expect(res === '').to.be.equal(true); 18 | done(); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/units/myIpAddress.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/myIpAddress.js'), timeout: 1000}); 6 | 7 | describe('#myIpAddress', function() { 8 | it('unusable', function(done) { 9 | testPac.FindWhistleProxyForURL('www.ifeng.com', function(err, res) { 10 | expect(res === 'socks://myipaddress.com:8080').to.be.equal(true); 11 | done(); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/units/shExpMatch.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/shExpMatch.js'), timeout: 1000}); 6 | 7 | describe('#shExpMatch', function() { 8 | it('*', function(done) { 9 | testPac.FindWhistleProxyForURL('http://www.test.com', function(err, res) { 10 | expect(res === 'proxy://shexpmatch1.com:8080').to.be.equal(true); 11 | done(); 12 | }); 13 | }); 14 | it('*', function(done) { 15 | testPac.FindWhistleProxyForURL('http://.test.com', function(err, res) { 16 | expect(res === 'proxy://shexpmatch1.com:8080').to.be.equal(true); 17 | done(); 18 | }); 19 | }); 20 | it('?', function(done) { 21 | testPac.FindWhistleProxyForURL('http://1.test2.com', function(err, res) { 22 | expect(res).to.be.equal('proxy://shexpmatch2.com:8080'); 23 | done(); 24 | }); 25 | }); 26 | it('others', function(done) { 27 | testPac.FindWhistleProxyForURL('http://wwwitesticom', function(err, res) { 28 | expect(res === '').to.be.equal(true); 29 | done(); 30 | }); 31 | }); 32 | it('others', function(done) { 33 | testPac.FindWhistleProxyForURL('http://22.test2.com', function(err, res) { 34 | expect(res === '').to.be.equal(true); 35 | done(); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/units/timeRange.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/timeRange.js'), timeout: 1000}); 6 | 7 | describe('#timeRange', function() { 8 | it('not in the range', function(done) { 9 | testPac.FindWhistleProxyForURL('www.ifeng.com', function(err, res) { 10 | expect(res).to.be.equal('proxy://timerange.com:8080'); 11 | done(); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/units/weekdayRange.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var Pac = require('../../index'); 5 | var testPac = new Pac({url: path.join(__dirname, '../scripts/weekdayRange.js'), timeout: 1000}); 6 | 7 | describe('#weekdayRange', function() { 8 | it('in the range', function(done) { 9 | testPac.FindWhistleProxyForURL('www.ifeng.com', function(err, res) { 10 | expect(res).to.be.equal('socks://weekdayrange.com:8080'); 11 | done(); 12 | }); 13 | }); 14 | }); 15 | --------------------------------------------------------------------------------