whistle 2.10.1 → 2.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/assets/js/log.js +44 -27
  2. package/assets/menu.html +2 -0
  3. package/assets/modal.html +2 -0
  4. package/assets/tab.html +3 -3
  5. package/bin/use.js +236 -101
  6. package/bin/util.js +10 -7
  7. package/bin/whistle.js +5 -5
  8. package/biz/webui/cgi-bin/download.js +1 -1
  9. package/biz/webui/cgi-bin/get-data.js +0 -5
  10. package/biz/webui/cgi-bin/init.js +0 -2
  11. package/biz/webui/cgi-bin/rules/clear-dns-cache.js +7 -0
  12. package/biz/webui/htdocs/index.html +2 -2
  13. package/biz/webui/htdocs/js/index.js +53 -50
  14. package/biz/webui/lib/index.js +49 -4
  15. package/lib/config.js +6 -4
  16. package/lib/handlers/file-proxy.js +1 -1
  17. package/lib/https/ca.js +39 -9
  18. package/lib/https/index.js +109 -114
  19. package/lib/index.js +1 -5
  20. package/lib/inspectors/data.js +119 -13
  21. package/lib/inspectors/log.js +4 -0
  22. package/lib/inspectors/req.js +5 -3
  23. package/lib/inspectors/weinre.js +7 -2
  24. package/lib/plugins/get-plugins-sync.js +15 -11
  25. package/lib/plugins/get-plugins.js +11 -12
  26. package/lib/plugins/load-plugin.js +1 -1
  27. package/lib/plugins/module-paths.js +4 -0
  28. package/lib/plugins/util.js +8 -0
  29. package/lib/rules/dns.js +4 -0
  30. package/lib/rules/index.js +0 -1
  31. package/lib/rules/protocols.js +3 -9
  32. package/lib/rules/rules.js +21 -22
  33. package/lib/service/composer.js +10 -5
  34. package/lib/{util/log-server.js → service/console-log.js} +37 -34
  35. package/lib/service/index.js +2 -4
  36. package/lib/service/service.js +64 -15
  37. package/lib/service/util.js +3 -42
  38. package/lib/socket-mgr.js +2 -16
  39. package/lib/tunnel.js +2 -4
  40. package/lib/upgrade.js +1 -1
  41. package/lib/util/common.js +115 -17
  42. package/lib/util/index.js +48 -40
  43. package/package.json +2 -2
  44. package/biz/webui/cgi-bin/import-remote.js +0 -37
  45. package/biz/webui/cgi-bin/log/set.js +0 -7
  46. package/lib/util/parse-query.js +0 -35
@@ -10,7 +10,9 @@ var MAX_REQ_BODY_SIZE = (config.strict ? 256 : 2048) * 1024;
10
10
  var MAX_RES_BODY_SIZE = (config.strict ? 256 : 2048) * 1024;
11
11
  var BIG_DATA_SIZE = 1024 * 1024 * 16;
12
12
  var LOCALHOST = '127.0.0.1';
13
- var SSE_RE = /^\s*text\/event-stream/i;
13
+ var MAX_PAYLOAD = util.MAX_FRAME_PAYLOAD;
14
+ var SSE_SEP = Buffer.from('\n\n');
15
+ var FRAME_SEP_KEY = 'x-whistle-custom-frame-separator';
14
16
 
15
17
  function getZipType(options) {
16
18
  return options.headers && options.headers['content-encoding'];
@@ -57,6 +59,80 @@ function emitDataEvents(req, res, proxy) {
57
59
  if (!util.showPluginReq(req) || util.isHide(req)) {
58
60
  return;
59
61
  }
62
+
63
+ var reqSepInfo = parseFrameSep(req.headers);
64
+ var cseSep = reqSepInfo && reqSepInfo.sep;
65
+ var resSepInfo;
66
+
67
+ function emitFrame(chunk, fromServer) {
68
+ proxy.emit('frame', {
69
+ reqId: req.reqId,
70
+ frameId: util.getFrameId(),
71
+ isClient: !fromServer,
72
+ length: chunk.length,
73
+ bin: chunk
74
+ });
75
+ }
76
+
77
+ function parseFrameSep(headers) {
78
+ var sep = headers[FRAME_SEP_KEY];
79
+ if (!util.isString(sep)) {
80
+ return;
81
+ }
82
+ var result = {};
83
+ delete headers[FRAME_SEP_KEY];
84
+ try {
85
+ sep = decodeURIComponent(sep);
86
+ } catch (e) {}
87
+ if (sep[0] === '/') {
88
+ result.keepSep = true;
89
+ sep = sep.slice(1);
90
+ }
91
+ if (sep) {
92
+ result.sep = Buffer.from(sep);
93
+ result.sepLen = sep.length;
94
+ }
95
+ return result;
96
+ }
97
+
98
+ function parseFrame(buf, isRes, end) {
99
+ var len = buf && buf.length;
100
+ if (!len) {
101
+ return null;
102
+ }
103
+ var sep, sepLen, keepSep;
104
+ var info = isRes ? resSepInfo : reqSepInfo;
105
+ if (info) {
106
+ sep = info.sep;
107
+ sepLen = info.sepLen;
108
+ keepSep = info.keepSep;
109
+ } else {
110
+ sep = SSE_SEP;
111
+ sepLen = 2;
112
+ }
113
+ var index = buf.indexOf(sep);
114
+ while (index !== -1) {
115
+ if (keepSep) {
116
+ index += sepLen;
117
+ }
118
+ index && emitFrame(buf.slice(0, index), isRes);
119
+ buf = buf.slice(keepSep ? index : index + sepLen);
120
+ index = buf.indexOf(sep);
121
+ }
122
+ len = buf.length;
123
+ if (!len) {
124
+ return null;
125
+ }
126
+ if (end) {
127
+ emitFrame(buf, isRes);
128
+ return null;
129
+ }
130
+ if (len > MAX_PAYLOAD) {
131
+ emitFrame(buf.slice(0, -1), isRes);
132
+ buf = buf.slice(-1);
133
+ }
134
+ return buf;
135
+ }
60
136
  var getTTFB = function () {
61
137
  if (req._ttfb >= now) {
62
138
  return req._ttfb - now;
@@ -167,12 +243,20 @@ function emitDataEvents(req, res, proxy) {
167
243
  var captureReqStream;
168
244
  var maxReqSize;
169
245
  var reqInfo;
246
+ var setReqBody = function (body, end) {
247
+ if (captureReqStream && cseSep) {
248
+ reqBody = parseFrame(reqBody, false, end);
249
+ } else {
250
+ reqData.body = reqBody;
251
+ }
252
+ };
170
253
  var updateReqInfo = function() {
171
254
  if (reqInfo && !captureReqStream) {
172
255
  captureReqStream = !disable.captureStream && (resDone || enable.captureStream) && !getZipType(reqInfo);
173
256
  maxReqSize = useBigData ? BIG_DATA_SIZE : (captureReqStream ? MAX_REQ_BODY_SIZE : MAX_SIZE);
174
- if (captureReqStream && reqBody && !reqData.body) {
175
- reqData.body = reqBody;
257
+ if (captureReqStream) {
258
+ data.isCse = !!cseSep;
259
+ reqBody && !reqData.body && setReqBody(reqBody);
176
260
  }
177
261
  }
178
262
  };
@@ -198,7 +282,7 @@ function emitDataEvents(req, res, proxy) {
198
282
  } else {
199
283
  reqBody = reqBody ? Buffer.concat([reqBody, chunk]) : chunk;
200
284
  if (captureReqStream) {
201
- reqData.body = reqBody;
285
+ setReqBody(reqBody);
202
286
  }
203
287
  if (reqBody.length > maxReqSize) {
204
288
  reqBody = false;
@@ -225,8 +309,8 @@ function emitDataEvents(req, res, proxy) {
225
309
  data.endTime = endTime;
226
310
  }
227
311
  reqBody = err ? util.getErrorStack(err) : body;
228
- reqData.body = reqBody;
229
- setUnzipSize(body, reqData);
312
+ setReqBody(reqBody, true);
313
+ !data.isCse && setUnzipSize(body, reqData);
230
314
  checkBodySize(reqData, useBigData);
231
315
  });
232
316
  updateEvent && proxy.emit(updateEvent, req.fullUrl, false, true);
@@ -239,9 +323,22 @@ function emitDataEvents(req, res, proxy) {
239
323
  }
240
324
  resDone = true;
241
325
  info = info || (res._needGunzip ? { statusCode: res.statusCode, headers: '' } : res);
242
- var captureStream = !disable.captureStream && (requestTime == null || enable.captureStream ||
243
- (info.headers && SSE_RE.test(info.headers['content-type']))) && !getZipType(info);
326
+ var isSse;
327
+ var captureStream;
328
+ if (!disable.captureStream && !getZipType(info)) {
329
+ var headers = info.headers;
330
+ captureStream = requestTime == null || enable.captureStream;
331
+ if (headers) {
332
+ isSse = headers['content-type'] === 'text/event-stream';
333
+ captureStream = captureStream || isSse;
334
+ resSepInfo = parseFrameSep(headers);
335
+ if (resSepInfo && resSepInfo.sep) {
336
+ isSse = captureStream;
337
+ }
338
+ }
339
+ }
244
340
  var maxResSize = useBigData ? BIG_DATA_SIZE : (captureStream ? MAX_RES_BODY_SIZE : MAX_SIZE);
341
+ data.isSse = isSse;
245
342
  captureStream && updateReqInfo();
246
343
  if (!cleared && util.hasBody(info, req) && checkType(info)) {
247
344
  if (info.headers['content-length'] > maxResSize && !captureStream) {
@@ -261,9 +358,13 @@ function emitDataEvents(req, res, proxy) {
261
358
  } else {
262
359
  resBody = resBody ? Buffer.concat([resBody, chunk]) : chunk;
263
360
  if (captureStream) {
264
- resData.body = resBody;
361
+ if (isSse) {
362
+ resBody = parseFrame(resBody, true);
363
+ } else {
364
+ resData.body = resBody;
365
+ }
265
366
  }
266
- if (resBody.length > maxResSize) {
367
+ if (resBody && resBody.length > maxResSize) {
267
368
  resBody = false;
268
369
  }
269
370
  }
@@ -286,9 +387,13 @@ function emitDataEvents(req, res, proxy) {
286
387
  resData.hasGzipError = unzipBody(info, resBody, function (err, body) {
287
388
  if (!data.resError) {
288
389
  resBody = err ? util.getErrorStack(err) : body;
289
- resData.body = resBody;
390
+ if (isSse) {
391
+ resBody = parseFrame(resBody, true, true);
392
+ } else {
393
+ resData.body = resBody;
394
+ }
290
395
  }
291
- setUnzipSize(body, resData);
396
+ !isSse && setUnzipSize(body, resData);
292
397
  checkBodySize(resData, useBigData);
293
398
  setEndTime();
294
399
  reqEmitter.emit('end', data);
@@ -341,7 +446,8 @@ function emitDataEvents(req, res, proxy) {
341
446
  }
342
447
  !endTime && setEndTime();
343
448
  delete data.abort;
344
- if (err && err.message !== 'Aborted') {
449
+ var msg = err && err.message;
450
+ if (msg !== 'Aborted') {
345
451
  data.resError = true;
346
452
  if (resData.body) {
347
453
  data.errMsg = util.getErrorStack(err);
@@ -29,7 +29,11 @@ module.exports = function (req, res) {
29
29
  if (log) {
30
30
  util.disableReqCache(req.headers);
31
31
  var host = req.headers.host;
32
+ delete req.rules.log;
32
33
  res.on('src', function (_res) {
34
+ if (!(log = req.rules.log) || !util.hasBody(_res)) {
35
+ return;
36
+ }
33
37
  var topScript, isHtml;
34
38
  if (util.supportHtmlTransform(_res, req)) {
35
39
  isHtml = true;
@@ -147,9 +147,11 @@ function handleReq(req, data, reqRules, delType) {
147
147
  }
148
148
  }
149
149
 
150
- function handleAuth(data, auth) {
151
- auth = util.getAuthBasic(auth);
152
- auth && util.setHeader(data, 'authorization', auth);
150
+ function handleAuth(data, authObj) {
151
+ var auth = util.getAuthBasic(authObj);
152
+ if (auth) {
153
+ util.setHeader(data, (authObj.proxy ? 'proxy-' : '') + 'authorization', auth);
154
+ }
153
155
  }
154
156
 
155
157
  function handleParams(req, params, urlParams, enableBigData) {
@@ -21,17 +21,22 @@ function getScript(host, name, isHtml, req) {
21
21
  }
22
22
 
23
23
  module.exports = function (req, res) {
24
- if (req.rules.weinre) {
24
+ var weinre = req.rules.weinre;
25
+ if (weinre) {
25
26
  util.disableReqCache(req.headers);
27
+ delete req.rules.weinre;
26
28
  var host = req.headers.host;
27
29
  res.on('src', function (_res) {
30
+ if (!(weinre = req.rules.weinre) || !util.hasBody(_res)) {
31
+ return;
32
+ }
28
33
  var isHtml = util.supportHtmlTransform(_res, req);
29
34
  if (!isHtml && util.getContentType(_res.headers) !== 'JS') {
30
35
  return;
31
36
  }
32
37
  !util.isEnable(req, 'keepAllCSP') && util.disableCSP(_res.headers);
33
38
  !req._customCache && util.disableResStore(_res.headers);
34
- var name = util.getPath(util.rule.getMatcher(req.rules.weinre));
39
+ var name = util.getPath(util.rule.getMatcher(weinre));
35
40
  var transform = new Transform();
36
41
  transform._transform = function (chunk, _, callback) {
37
42
  if (!chunk) {
@@ -12,6 +12,7 @@ var notUninstallPluginPaths = config.notUninstallPluginPaths || [];
12
12
  var projectPluginPaths = config.projectPluginPaths || [];
13
13
  var accountPluginsPath = config.accountPluginsPath || [];
14
14
  var paths = mp.getPaths();
15
+ var PLUGIN_DIR = pluginUtil.PLUGIN_DIR;
15
16
 
16
17
  function getMTimeSync(filePath) {
17
18
  var stats = common.getStatSync(filePath);
@@ -41,8 +42,9 @@ function readPluginModulesSync(dir, plugins) {
41
42
  var mtime = getMTimeSync(path.join(root, 'package.json'));
42
43
  if (mtime != null) {
43
44
  var old = plugins[name];
44
- if (!old || (mtime > old.mtime && root === old.root)) {
45
+ if (pluginUtil.compparePlugin(dir, mtime, old)) {
45
46
  plugins[name] = {
47
+ [PLUGIN_DIR]: dir,
46
48
  mtime: mtime,
47
49
  root: root,
48
50
  account: account,
@@ -65,8 +67,9 @@ function readPluginModulesSync(dir, plugins) {
65
67
  var mtime = getMTimeSync(path.join(root, 'package.json'));
66
68
  if (mtime != null) {
67
69
  var old = plugins[name];
68
- if (!old || (mtime > old.mtime && root === old.root)) {
70
+ if (pluginUtil.compparePlugin(dir, mtime, old)) {
69
71
  plugins[name] = {
72
+ [PLUGIN_DIR]: dir,
70
73
  root: root,
71
74
  mtime: mtime,
72
75
  account: account,
@@ -95,15 +98,15 @@ module.exports = function () {
95
98
  if (pluginUtil.excludePlugin(simpleName)) {
96
99
  return;
97
100
  }
98
- var dir = plugins[name];
99
- var account = dir.account;
100
- var isSys = dir.isSys;
101
- var isProj = dir.isProj;
102
- var notUn = dir.notUn;
103
- var isDev = dir.isDev;
104
- var dirName = dir.dir;
105
- var mtime = dir.mtime;
106
- dir = dir.root;
101
+ var obj = plugins[name];
102
+ var account = obj.account;
103
+ var isSys = obj.isSys;
104
+ var isProj = obj.isProj;
105
+ var notUn = obj.notUn;
106
+ var isDev = obj.isDev;
107
+ var dirName = obj.dir;
108
+ var mtime = obj.mtime;
109
+ var dir = obj.root;
107
110
  var pkgPath = path.join(dir, 'package.json');
108
111
  var pkg = common.readJsonSync(pkgPath);
109
112
  if (!pkg) {
@@ -120,6 +123,7 @@ module.exports = function () {
120
123
  var tabs = util.getInspectorTabs(conf);
121
124
  var hintList = util.getHintList(conf);
122
125
  var plugin = {
126
+ [PLUGIN_DIR]: obj[PLUGIN_DIR],
123
127
  account: account,
124
128
  dir: dirName,
125
129
  isSys: isSys,
@@ -9,6 +9,7 @@ var customPluginPaths = config.customPluginPaths || [];
9
9
  var notUninstallPluginPaths = config.notUninstallPluginPaths || [];
10
10
  var projectPluginPaths = config.projectPluginPaths || [];
11
11
  var accountPluginsPath = config.accountPluginsPath || [];
12
+ var PLUGIN_DIR = pluginUtil.PLUGIN_DIR;
12
13
 
13
14
  function readPluginRootList(dir, callback) {
14
15
  var roots = [];
@@ -31,7 +32,7 @@ function readPluginRootList(dir, callback) {
31
32
  if (pluginUtil.isWhistleModule(name)) {
32
33
  roots[i] = {
33
34
  name: name,
34
- dir: dir
35
+ [PLUGIN_DIR]: dir
35
36
  };
36
37
  } else if (pluginUtil.isOrgModule(name)) {
37
38
  ++count;
@@ -44,7 +45,7 @@ function readPluginRootList(dir, callback) {
44
45
  orgList.push({
45
46
  org: name,
46
47
  name: pluginName,
47
- dir: dir
48
+ [PLUGIN_DIR]: dir
48
49
  });
49
50
  }
50
51
  });
@@ -74,7 +75,7 @@ function readPluginModules(dir, callback, plugins, isSys) {
74
75
  return callback(plugins);
75
76
  }
76
77
  list.forEach(function (obj) {
77
- var dir = obj.dir;
78
+ var dir = obj[PLUGIN_DIR];
78
79
  var dirName = obj.org ? obj.org + '/' + obj.name : obj.name;
79
80
  pluginUtil.getSysPath(dir, dirName, isSys, function(root) {
80
81
  common.getStat(path.join(root, 'package.json'), function (_, stats) {
@@ -83,13 +84,10 @@ function readPluginModules(dir, callback, plugins, isSys) {
83
84
  obj.mtime = stats.mtime.getTime();
84
85
  }
85
86
  if (--len === 0) {
86
- list.forEach(function (obj) {
87
- var name = pluginUtil.getPluginName(obj.name);
88
- if (obj.root) {
89
- var old = plugins[name];
90
- if (!old || (obj.mtime > old.mtime && obj.root === old.root)) {
91
- plugins[name] = obj;
92
- }
87
+ list.forEach(function (item) {
88
+ var name = pluginUtil.getPluginName(item.name);
89
+ if (item.root && pluginUtil.compparePlugin(dir, item.mtime, plugins[name])) {
90
+ plugins[name] = item;
93
91
  }
94
92
  });
95
93
  callback(plugins);
@@ -121,10 +119,11 @@ module.exports = function (callback) {
121
119
  if (pluginUtil.excludePlugin(name)) {
122
120
  return;
123
121
  }
124
- var old = result[name + ':'];
125
122
  var obj = plugins[name];
126
- if (!old || (obj.mtime > old.mtime && obj.root === old.path)) {
123
+ var dir = obj[PLUGIN_DIR];
124
+ if (pluginUtil.compparePlugin(dir, obj.mtime, result[name + ':'])) {
127
125
  result[name + ':'] = {
126
+ [PLUGIN_DIR]: dir,
128
127
  account: account,
129
128
  dir: config.whistleName,
130
129
  isSys: isSys,
@@ -1202,7 +1202,7 @@ module.exports = async function (options, callback) {
1202
1202
  PROXY_ID_HEADER = config.PROXY_ID_HEADER;
1203
1203
  options.getTempFilePath = function(filePath) {
1204
1204
  if (common.TEMP_PATH_RE.test(filePath)) {
1205
- return path.join(config.TEMP_FILES_PATH, RegExp.$1);
1205
+ return path.join(common.TEMP_FILES_PATH, RegExp.$1);
1206
1206
  }
1207
1207
  };
1208
1208
 
@@ -36,6 +36,7 @@ var pluginPaths = config.pluginPaths;
36
36
  if (pluginPaths) {
37
37
  pluginPaths = pluginPaths.concat(pluginPaths.map(formatPath).concat(addon));
38
38
  pluginPaths = prePlugins.concat(pluginPaths);
39
+ config.PLUGIN_INSTALL_ROOT = pluginPaths[0];
39
40
  addDebugPaths(pluginPaths);
40
41
  pluginPaths = uniqueArr(pluginPaths);
41
42
  exports.getPaths = function () {
@@ -80,6 +81,9 @@ if (pluginsPath && paths.indexOf(pluginsPath) == -1) {
80
81
  }
81
82
  if (!config.customPluginPaths || !config.customPluginPaths.length) {
82
83
  paths.unshift(config.CUSTOM_PLUGIN_PATH);
84
+ config.PLUGIN_INSTALL_ROOT = config.CUSTOM_PLUGIN_PATH;
85
+ } else {
86
+ config.PLUGIN_INSTALL_ROOT = config.customPluginPaths[0];
83
87
  }
84
88
  paths = addon.concat(paths);
85
89
  addDebugPaths(paths);
@@ -13,11 +13,14 @@ var MAX_EXPIRE = 36000;
13
13
  var UTF8_OPTIONS = { encoding: 'utf8' };
14
14
  var workerScript = util.readFileSync(path.join(__dirname, '../../assets/js/worker.js'), false);
15
15
  var workers = {};
16
+ var PLUGIN_DIR = Symbol('dir');
16
17
 
17
18
  function replaceScript(ctn, name) {
18
19
  return workerScript.replace('$PLUGIN_NAME', name).replace('/*sourcecode*/', ctn);
19
20
  }
20
21
 
22
+ exports.PLUGIN_DIR = PLUGIN_DIR;
23
+
21
24
  exports.readWorkerSync = function(root, conf, name) {
22
25
  var file = util.getWebWorker(conf);
23
26
  file = file && util.readFileSync(path.join(root, file));
@@ -140,6 +143,7 @@ function setPlugin(plugins, pkg, root, mtime, sync) {
140
143
  if (PLUGIN_NAME_RE.test(pkg.name)) {
141
144
  var name = sync ? RegExp.$1 : getPluginName(RegExp.$1);
142
145
  plugins[name] = {
146
+ [PLUGIN_DIR]: DEV_PLUGINS_PATH,
143
147
  root: root,
144
148
  mtime: mtime,
145
149
  notUn: true,
@@ -256,3 +260,7 @@ exports.getSysPath = function(dir, name, isSys, cb) {
256
260
  cb(exists ? sysRoot : root);
257
261
  });
258
262
  };
263
+
264
+ exports.compparePlugin = function (dir, mtime, old) {
265
+ return !old || (mtime > old.mtime && dir === old[PLUGIN_DIR]);
266
+ };
package/lib/rules/dns.js CHANGED
@@ -177,3 +177,7 @@ module.exports = function lookup(hostname, callback, allowDnsCache) {
177
177
  err ? lookupDNS(hostname, callback) : callback(err, ip);
178
178
  });
179
179
  };
180
+
181
+ module.exports.clearCache = function() {
182
+ dnsCache = {};
183
+ };
@@ -63,7 +63,6 @@ exports.hasReqScript = rules.hasReqScript.bind(rules);
63
63
  exports.resolveDisable = rules.resolveDisable.bind(rules);
64
64
  exports.resolvePipe = rules.resolvePipe.bind(rules);
65
65
  exports.resolveRule = rules.resolveRule.bind(rules);
66
- exports.resolveRules = resolveReqRules;
67
66
  exports.resolveResRules = rules.resolveResRules.bind(rules);
68
67
  exports.resolveBodyFilter = rules.resolveBodyFilter.bind(rules);
69
68
  exports.lookupHost = rules.lookupHost.bind(rules);
@@ -73,6 +73,7 @@ var protocols = [
73
73
  'cipher',
74
74
  'sniCallback'
75
75
  ];
76
+ var toolProtocols = ['log', 'weinre'];
76
77
 
77
78
  var RULE_RE = /^(?:|x|xs)(?:file|rawfile|dust|tpl|jsonp):/;
78
79
  var LOC_RE = /^locationHref:/;
@@ -86,7 +87,6 @@ var pureResProtocols = [
86
87
  'resDelay',
87
88
  'resSpeed',
88
89
  'resType',
89
- 'resType',
90
90
  'resCharset',
91
91
  'resCookies',
92
92
  'resCors',
@@ -108,7 +108,7 @@ var pureResProtocols = [
108
108
  'htmlPrepend',
109
109
  'jsPrepend',
110
110
  'responseFor'
111
- ];
111
+ ].concat(toolProtocols);
112
112
  var resProtocols = [
113
113
  'filter',
114
114
  'enable',
@@ -155,7 +155,7 @@ var aliasProtocols = {
155
155
  var protocolsWithoutG = protocols.slice(1);
156
156
  var reqProtocols = protocols.filter(function (name) {
157
157
  return pureResProtocols.indexOf(name) === -1;
158
- });
158
+ }).concat(toolProtocols);
159
159
  var reqProtosWithoutG = reqProtocols.filter(function (name) {
160
160
  return name !== 'G';
161
161
  });
@@ -266,12 +266,6 @@ function resetRules(rules) {
266
266
 
267
267
  exports.resetRules = resetRules;
268
268
 
269
- function isResRule(protocol) {
270
- return resProtocols.indexOf(protocol) != -1;
271
- }
272
-
273
- exports.isResRule = isResRule;
274
-
275
269
  function isWebProtocol(protocol) {
276
270
  return protocol == 'http:' || protocol == 'https:';
277
271
  }
@@ -14,7 +14,6 @@ var env = process.env || {};
14
14
  var allowDnsCache = true;
15
15
  var SUB_MATCH_RE = /\$[&\d]/;
16
16
  var BODY_MATCH_RE = /\$b[&\d]/;
17
- var SPACE_RE = /\s+/g;
18
17
  var EXACT_RE = /^\$/;
19
18
  var NON_RE = /^!/;
20
19
  var HAS_SPACE_RE = /\s/;
@@ -98,6 +97,7 @@ var FILE_PROTO_RE = /^x?((raw)?file|tpl|jsonp|dust):\/\//;
98
97
  var NO_PROTO_RE = /[^\w!*|.-]/;
99
98
  var SKIP_RE = /^skip:\/\//;
100
99
  var SUB_VAR_RE = /\$\{RegExp\.\$([&\d])\}/g;
100
+ var TOOL_RE = /^(?:log|weinre):\/\//;
101
101
  var mIndex = 0;
102
102
  var mNow = Date.now();
103
103
  var IS_JSON = Symbol('isJson');
@@ -370,7 +370,7 @@ function joinUrl(a, b) {
370
370
  }
371
371
 
372
372
  function toLine(_, line) {
373
- return line.replace(SPACE_RE, ' ');
373
+ return line.replace(/\s+/g, ' ');
374
374
  }
375
375
 
376
376
  function mergeLines(text) {
@@ -799,18 +799,12 @@ function getValueFor(key, vals, file) {
799
799
  return val;
800
800
  }
801
801
 
802
- function getRule(req, list, vals, index, isFilter, host) {
803
- var rule = resolveRuleList(req, list, vals, index || 0, isFilter, null, host);
802
+ function getRule(req, list, vals, index, isFilter, host, isReq) {
803
+ var rule = resolveRuleList(req, list, vals, index || 0, isFilter, null, host, isReq);
804
804
  resolveValue(rule, vals, req);
805
805
  return rule;
806
806
  }
807
807
 
808
- function getRuleList(req, list, vals, isEnableProxy) {
809
- return resolveRuleList(req, list, vals, isEnableProxy).map(function (rule) {
810
- return resolveValue(rule, vals, req);
811
- });
812
- }
813
-
814
808
  function resolveValue(rule, vals, req) {
815
809
  if (!rule) {
816
810
  return;
@@ -924,7 +918,7 @@ function checkInternal(req, rule) {
924
918
  return props.internalOnly;
925
919
  }
926
920
 
927
- function resolveRuleList(req, list, vals, index, isFilter, isEnableProxy, host) {
921
+ function resolveRuleList(req, list, vals, index, isFilter, isEnableProxy, host, isReq) {
928
922
  var curUrl = util.formatUrl(req.curUrl);
929
923
  var notHttp = list.isRuleProto && curUrl[0] !== 'h';
930
924
  //支持域名匹配
@@ -986,6 +980,9 @@ function resolveRuleList(req, list, vals, index, isFilter, isEnableProxy, host)
986
980
  if (notHttp && protoMgr.isFileProxy(rule.matcher)) {
987
981
  return false;
988
982
  }
983
+ if (isReq && TOOL_RE.test(rule.matcher)) {
984
+ return true;
985
+ }
989
986
  return (isFilter || !matchExcludeFilters(curUrl, rule, req)) && (host == null || util.checkProxyHost(rule, host));
990
987
  };
991
988
 
@@ -1155,7 +1152,7 @@ function resolveProps(req, rules, vals, isIgnore) {
1155
1152
  return result;
1156
1153
  }
1157
1154
  }
1158
- return util.resolveProperties(list, result);
1155
+ return util.parseRuleProps(list, result);
1159
1156
  }
1160
1157
 
1161
1158
  function parseWildcard(pattern, not) {
@@ -1868,7 +1865,7 @@ function matchFilter(url, filter, req) {
1868
1865
  if (keyLen > 0 && key[keyLen] === '%') {
1869
1866
  key = key.substring(0, keyLen) / 100;
1870
1867
  }
1871
- return getFilterResult(Math.random() < chance.key, filter);
1868
+ return getFilterResult(Math.random() < key, filter);
1872
1869
  }
1873
1870
  if (filterProp(req.method, filter.method, filter.mPattern)) {
1874
1871
  return getFilterResult(result, filter);
@@ -1967,7 +1964,7 @@ function matchFilter(url, filter, req) {
1967
1964
  return false;
1968
1965
  }
1969
1966
 
1970
- function matchExcludeFilters(url, rule, options) {
1967
+ function matchExcludeFilters(url, rule, req) {
1971
1968
  var filters = rule.filters;
1972
1969
  if (!filters) {
1973
1970
  return;
@@ -1980,7 +1977,7 @@ function matchExcludeFilters(url, rule, options) {
1980
1977
  hasIncludeFilter = hasIncludeFilter || filter.isInclude;
1981
1978
  if (
1982
1979
  (filter.isInclude ? !include : !exclude) &&
1983
- matchFilter(url, filter, options)
1980
+ matchFilter(url, filter, req)
1984
1981
  ) {
1985
1982
  if (filter.isInclude) {
1986
1983
  include = true;
@@ -2249,7 +2246,7 @@ function resolveRules(req, isReq, isRes) {
2249
2246
  name === 'rule' ||
2250
2247
  name === 'plugin' ||
2251
2248
  !filter[name]) &&
2252
- (rule = self.getRule(req, rules[name], vals))
2249
+ (rule = self.getRule(req, rules[name], vals, null, null, null, isReq))
2253
2250
  ) {
2254
2251
  _rules[name] = rule;
2255
2252
  }
@@ -2283,15 +2280,17 @@ function resolveRules(req, isReq, isRes) {
2283
2280
  return _rules;
2284
2281
  }
2285
2282
 
2286
- proto.getRule = function(req, list, vals, index, isFilter, host) {
2283
+ proto.getRule = function(req, list, vals, index, isFilter, host, isReq) {
2287
2284
  if (!this._isGlobal || !req._disabledProxyRules) {
2288
- return getRule(req, list, vals, index, isFilter, host);
2285
+ return getRule(req, list, vals, index, isFilter, host, isReq);
2289
2286
  }
2290
2287
  };
2291
2288
 
2292
2289
  proto.getRuleList = function(req, list, vals, isEnableProxy) {
2293
2290
  if (!this._isGlobal || !req._disabledProxyRules) {
2294
- return getRuleList(req, list, vals, isEnableProxy);
2291
+ return resolveRuleList(req, list, vals, null, false, isEnableProxy).map(function (rule) {
2292
+ return resolveValue(rule, vals, req);
2293
+ });
2295
2294
  }
2296
2295
  return [];
2297
2296
  };
@@ -2341,8 +2340,8 @@ proto.resolveProxyProps = function (req) {
2341
2340
  }
2342
2341
  mergeRule(req.rules, enable, 'enable');
2343
2342
  mergeRule(req.rules, disable, 'disable');
2344
- enable = util.resolveProperties(enable);
2345
- disable = util.resolveProperties(disable);
2343
+ enable = util.parseRuleProps(enable);
2344
+ disable = util.parseRuleProps(disable);
2346
2345
  if (disable.clientId || disable.clientID || disable.clientid) {
2347
2346
  req.disable.clientId = true;
2348
2347
  }
@@ -2417,7 +2416,7 @@ proto.hasReqScript = function (req) {
2417
2416
  : this.getRule(req, this._rules.rulesFile, this._values);
2418
2417
  };
2419
2418
 
2420
- proto.resolveProxy = function resolveProxy(req, host) {
2419
+ proto.resolveProxy = function (req, host) {
2421
2420
  var proxy = this.getRule(req, this._rules.proxy, this._values, null, null, host);
2422
2421
  var matcher = proxy && proxy.matcher;
2423
2422
  var name = matcher && matcher.substring(0, matcher.indexOf(':'));