whistle 2.9.109 → 2.10.1

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 (41) hide show
  1. package/assets/js/log.js +8 -4
  2. package/bin/util.js +1 -1
  3. package/biz/index.js +12 -10
  4. package/biz/webui/cgi-bin/certs/active.js +6 -0
  5. package/biz/webui/cgi-bin/certs/remove.js +0 -1
  6. package/biz/webui/cgi-bin/certs/upload.js +0 -1
  7. package/biz/webui/cgi-bin/util.js +7 -20
  8. package/biz/webui/htdocs/index.html +1 -1
  9. package/biz/webui/htdocs/js/index.js +43 -43
  10. package/biz/webui/lib/index.js +10 -13
  11. package/lib/config.js +17 -36
  12. package/lib/handlers/file-proxy.js +2 -1
  13. package/lib/handlers/http-proxy.js +1 -1
  14. package/lib/https/ca.js +34 -27
  15. package/lib/https/h2.js +1 -1
  16. package/lib/https/index.js +21 -17
  17. package/lib/index.js +1 -1
  18. package/lib/init.js +13 -12
  19. package/lib/inspectors/req.js +13 -10
  20. package/lib/inspectors/res.js +375 -390
  21. package/lib/inspectors/rules.js +11 -0
  22. package/lib/plugins/compat.js +108 -0
  23. package/lib/plugins/get-plugins-sync.js +1 -4
  24. package/lib/plugins/index.js +92 -284
  25. package/lib/plugins/load-plugin.js +205 -223
  26. package/lib/plugins/module-paths.js +3 -4
  27. package/lib/plugins/proxy.js +4 -7
  28. package/lib/rules/index.js +69 -66
  29. package/lib/rules/protocols.js +6 -0
  30. package/lib/rules/rules.js +44 -32
  31. package/lib/rules/util.js +30 -0
  32. package/lib/service/composer.js +5 -5
  33. package/lib/service/util.js +7 -21
  34. package/lib/tunnel.js +15 -12
  35. package/lib/upgrade.js +9 -10
  36. package/lib/util/common.js +171 -48
  37. package/lib/util/file-mgr.js +3 -7
  38. package/lib/util/index.js +101 -143
  39. package/package.json +1 -1
  40. package/README-en_US.md +0 -80
  41. package/biz/webui/htdocs/img/assistant.svg +0 -1
@@ -1,4 +1,5 @@
1
1
  var path = require('path');
2
+ var common = require('../util/common');
2
3
  var config = require('../config');
3
4
 
4
5
  var PLUGIN_DIR_RE = /[/\\]whistle\.[a-z\d_-]+[/\\]?$/;
@@ -117,11 +118,9 @@ function formatPath(dir, prefix) {
117
118
  return null;
118
119
  }
119
120
  if (/(?:^|\/|\\)node_modules[\\\/]?$/.test(dir)) {
120
- return dir.replace(/\\/g, '/');
121
+ return common.formatPathSep(dir);
121
122
  }
122
- return path
123
- .resolve(dir, typeof prefix === 'string' ? prefix : '', 'node_modules')
124
- .replace(/\\/g, '/');
123
+ return common.formatPathSep(path.resolve(dir, typeof prefix === 'string' ? prefix : '', 'node_modules'));
125
124
  }
126
125
 
127
126
  function getGlobalDir() {
@@ -93,12 +93,12 @@ function createSocket(opts, cb) {
93
93
  var clientIp = common.removeIPV6Prefix(opts.clientIp);
94
94
  var clientPort = opts.clientPort;
95
95
  if (clientIp && net.isIP(clientIp)) {
96
- headers['x-forwarded-for'] = clientIp;
96
+ headers[common.CLIENT_IP_HEADER] = clientIp;
97
97
  }
98
98
  if (clientPort > 0 && clientPort < 65536) {
99
99
  headers['x-whistle-client-port'] = clientPort;
100
100
  }
101
- headers['x-whistle-request-tunnel-ack'] = '1';
101
+ headers[common.ACK_HEADER] = '1';
102
102
  client = http.request({
103
103
  host: proxyIp,
104
104
  port: proxyPort,
@@ -110,15 +110,12 @@ function createSocket(opts, cb) {
110
110
  client.once('connect', function (res, socket) {
111
111
  socket.on('error', handleCb);
112
112
  if (res.statusCode !== 200) {
113
- var err = new Error(
114
- 'Tunneling socket could not be established, statusCode=' +
115
- res.statusCode
116
- );
113
+ var err = new Error('Tunneling socket could not be established, statusCode=' + res.statusCode);
117
114
  err.statusCode = res.statusCode;
118
115
  socket.destroy();
119
116
  return handleCb(err);
120
117
  }
121
- if (res.headers['x-whistle-allow-tunnel-ack']) {
118
+ if (res.headers[common.ALLOW_ACK]) {
122
119
  socket.write('1');
123
120
  }
124
121
  handleCb(null, socket, res);
@@ -55,6 +55,7 @@ exports.parse = rules.parse.bind(rules);
55
55
  exports.append = rules.append.bind(rules);
56
56
  exports.resolveSNICallback = rules.resolveSNICallback.bind(rules);
57
57
  exports.resolveHost = rules.resolveHost.bind(rules);
58
+ exports.getGRuleList = rules.getGRuleList.bind(rules);
58
59
  exports.resolveInternalHost = rules.resolveInternalHost.bind(rules);
59
60
  exports.resolveProxy = rules.resolveProxy.bind(rules);
60
61
  exports.resolveEnable = rules.resolveEnable.bind(rules);
@@ -153,12 +154,11 @@ exports.getProxy = function (url, req, callback) {
153
154
  } else {
154
155
  proxy = resolveRulesFromList([pRules, rules, fRules, hRules], 'resolveProxy', req, hostValue);
155
156
  }
156
- var proxyHostOnly = isLineProp(proxy, 'proxyHostOnly');
157
- var proxyHost =
158
- proxyHostOnly ||
159
- util.isEnable(req, 'proxyHost') ||
160
- isProxyEnable(req, 'proxyHost');
157
+ var proxyHostOnly;
158
+ var proxyHost = util.isEnable(req, 'proxyHost') || isProxyEnable(req, 'proxyHost');
161
159
  if (proxy) {
160
+ proxyHostOnly = isLineProp(proxy, 'proxyHostOnly');
161
+ proxyHost = proxyHost || proxyHostOnly;
162
162
  var protocol = proxy.matcher.substring(0, proxy.matcher.indexOf(':'));
163
163
  ignoreProxy =
164
164
  !filter['ignore:' + protocol] &&
@@ -252,14 +252,15 @@ exports.getProxy = function (url, req, callback) {
252
252
  if (!pacUrl) {
253
253
  return setHost() || callback();
254
254
  }
255
+ var auth;
255
256
  if (AUTH_RE.test(pacUrl)) {
256
- var auth = RegExp.$1;
257
- req._pacAuth = auth;
257
+ auth = RegExp.$1;
258
258
  pacUrl = pacUrl.substring(auth.length + 1);
259
259
  }
260
- pacUrl = util.isUrl(pacUrl)
261
- ? pacUrl
262
- : util.join(pacRule.root, pacUrl);
260
+ if (!util.isUrl(pacUrl) && !(pacUrl = util.joinPath(pacRule.root, pacUrl))) {
261
+ return setHost() || callback();
262
+ }
263
+ req._pacAuth = auth;
263
264
  var pac = cachedPacs[pacUrl];
264
265
  if (pac) {
265
266
  delete cachedPacs[pacUrl];
@@ -476,43 +477,49 @@ function handleDynamicRules(script, req, res, cb) {
476
477
  }, null, null, null, req);
477
478
  }
478
479
 
479
- function resolveRulesFile(req, callback) {
480
- if (req.rules.G) {
481
- var varMap = {};
482
- var globalValue;
483
- var pList;
484
- req.rules.G.list.forEach(function (item) {
485
- if (item.matcher[0] !== 'P') {
486
- if (!globalValue) {
487
- globalValue = item;
488
- }
480
+ function resolvePluginVars(req, list) {
481
+ var varMap = {};
482
+ var globalValue;
483
+ var pList;
484
+ list.forEach(function (item) {
485
+ if (item.matcher[0] !== 'P') {
486
+ if (!globalValue) {
487
+ globalValue = item;
488
+ }
489
+ return;
490
+ }
491
+ var value = util.getMatcherValue(item);
492
+ var index = value.indexOf(PLUGIN_VAR_RE.test(value) ? '.' : '=');
493
+ if (index !== -1) {
494
+ var name = value.substring(0, index);
495
+ var plugin = getPluginName(name);
496
+ if (!plugin) {
489
497
  return;
490
498
  }
491
- var value = util.getMatcherValue(item);
492
- var index = value.indexOf(PLUGIN_VAR_RE.test(value) ? '.' : '=');
493
- if (index !== -1) {
494
- var name = value.substring(0, index);
495
- var plugin = exports.getPlugin(name);
496
- if (!plugin || !plugin.pluginVars) {
497
- return;
498
- }
499
- value = value.substring(index + 1);
500
- if (value) {
501
- pList = pList || [];
502
- pList.push(item);
503
- var list = varMap[name] || [];
504
- varMap[name] = list;
505
- if (list.indexOf(value) === -1) {
506
- list.push(value);
507
- }
499
+ value = value.substring(index + 1);
500
+ if (value) {
501
+ pList = pList || [];
502
+ pList.push(item);
503
+ var list = varMap[name] || [];
504
+ varMap[name] = list;
505
+ if (list.indexOf(value) === -1) {
506
+ list.push(value);
508
507
  }
509
508
  }
510
- });
511
- req._pluginVars = varMap;
512
- req.rules.P = pList;
513
- req.rules.G = globalValue;
514
- req.globalValue = util.getMatcherValue(globalValue);
515
- }
509
+ }
510
+ });
511
+ req.rules = req.rules || {};
512
+ req._pluginVars = varMap;
513
+ req.rules.P = pList;
514
+ req.rules.G = globalValue;
515
+ req.globalValue = util.getMatcherValue(globalValue);
516
+ }
517
+
518
+ exports.resolvePluginVars = resolvePluginVars;
519
+
520
+ exports.resolveRulesFile = function (req, callback) {
521
+ !req._resolvedG && req.rules.G && resolvePluginVars(req, req.rules.G.list);
522
+ req._resolvedG = true;
516
523
  var rule = req.rules.rulesFile;
517
524
  handleDynamicRules(rule, req, null, function (text, vals) {
518
525
  if (text) {
@@ -528,9 +535,8 @@ function resolveRulesFile(req, callback) {
528
535
  util.mergeRules(req, text);
529
536
  callback();
530
537
  });
531
- }
538
+ };
532
539
 
533
- exports.resolveRulesFile = resolveRulesFile;
534
540
  exports.resolveResRulesFile = function (req, res, callback) {
535
541
  var rule = req.rules && req.rules.resScript;
536
542
  handleDynamicRules(
@@ -553,7 +559,7 @@ exports.resolveResRulesFile = function (req, res, callback) {
553
559
  function getValue(req, key, keep) {
554
560
  var value = req.headers[key];
555
561
  if (value) {
556
- if (!keep && config.strict) {
562
+ if (!req.fromComposer && !keep && (config.strict || (!config.enableRequestHeaderRules && !config.multiEnv))) {
557
563
  value = null;
558
564
  } else {
559
565
  req.rulesHeaders[key] = value;
@@ -566,6 +572,10 @@ function getValue(req, key, keep) {
566
572
  return value;
567
573
  }
568
574
 
575
+ function getPluginName(key) {
576
+ return exports.getPlugin(key);
577
+ }
578
+
569
579
  function initHeaderRules(req, needBodyFilters) {
570
580
  if (req._bodyFilters !== undefined) {
571
581
  return;
@@ -597,8 +607,15 @@ function initHeaderRules(req, needBodyFilters) {
597
607
  }
598
608
  }
599
609
  var curVars = rules._globalPluginVars;
610
+ var globalVars = {};
600
611
  var value;
601
- req._globalPluginVars = {};
612
+ req._globalPluginVars = globalVars;
613
+ Object.keys(curVars).forEach(function (key) {
614
+ if (getPluginName(key)) {
615
+ value = curVars[key];
616
+ globalVars[key] = value;
617
+ }
618
+ });
602
619
  if (ruleValue) {
603
620
  var file = 'Header Rules';
604
621
  var vals = util.toPrivateValues(util.parseJSON(kvHeader), file);
@@ -611,25 +628,11 @@ function initHeaderRules(req, needBodyFilters) {
611
628
  req._bodyFilters = rules._rules._bodyFilters.concat(bodyFilters);
612
629
  }
613
630
  var headerVars = rulesMgr._globalPluginVars;
614
- var vars = extend({}, curVars, headerVars);
615
- Object.keys(vars).forEach(function (key) {
616
- var plugin = exports.getPlugin(key);
617
- if (!plugin || !plugin.pluginVars) {
618
- return;
619
- }
620
- var headerVal = headerVars[key];
621
- var curVal = curVars[key];
622
- if (curVal && headerVal) {
623
- vars[key] = headerVal.concat(curVal);
624
- }
625
- value = vars[key];
626
- req._globalPluginVars[key] = value;
627
- });
628
- } else {
629
- Object.keys(curVars).forEach(function (key) {
630
- if (!exports.getPlugin || exports.getPlugin(key)) {
631
- value = curVars[key];
632
- req._globalPluginVars[key] = value;
631
+ Object.keys(headerVars).forEach(function (key) {
632
+ var headerVal = getPluginName(key) && headerVars[key];
633
+ if (headerVal) {
634
+ var curVal = curVars[key];
635
+ globalVars[key] = curVal ? curVal.concat(headerVal) : headerVal;
633
636
  }
634
637
  });
635
638
  }
@@ -152,9 +152,13 @@ var aliasProtocols = {
152
152
  includeFilter: 'filter',
153
153
  P: 'G'
154
154
  };
155
+ var protocolsWithoutG = protocols.slice(1);
155
156
  var reqProtocols = protocols.filter(function (name) {
156
157
  return pureResProtocols.indexOf(name) === -1;
157
158
  });
159
+ var reqProtosWithoutG = reqProtocols.filter(function (name) {
160
+ return name !== 'G';
161
+ });
158
162
 
159
163
  var RULE_PROTO_RE = /^([\w.-]+):\/\//;
160
164
 
@@ -222,8 +226,10 @@ exports.multiMatchs = [
222
226
  'resScript'
223
227
  ];
224
228
  exports.protocols = protocols;
229
+ exports.protocolsWithoutG = protocolsWithoutG;
225
230
  exports.pureResProtocols = pureResProtocols;
226
231
  exports.reqProtocols = reqProtocols;
232
+ exports.reqProtosWithoutG = reqProtosWithoutG;
227
233
  exports.resProtocols = resProtocols;
228
234
  exports.aliasProtocols = aliasProtocols;
229
235
 
@@ -27,7 +27,9 @@ var ROOT_PLUGIN_RE = /[\\/]whistle\.([a-z\d_-]+)$/;
27
27
  var TPL_KEY_RE = /\{\{\s*%?ruleValue\s*\}\}/g;
28
28
  var END_RE = /\$$/;
29
29
  var protocols = protoMgr.protocols;
30
+ var protocolsWithoutG = protoMgr.protocolsWithoutG;
30
31
  var reqProtocols = protoMgr.reqProtocols;
32
+ var reqProtosWithoutG = protoMgr.reqProtosWithoutG;
31
33
  var pureResProtocols = protoMgr.pureResProtocols;
32
34
  var multiMatchs = protoMgr.multiMatchs;
33
35
  var aliasProtocols = protoMgr.aliasProtocols;
@@ -36,7 +38,7 @@ var PROXY_RE =
36
38
  /^x?(socks|proxy|https?-proxy|internal-proxy|internal-https?-proxy|https2http-proxy|http2https-proxy):\/\//;
37
39
  var VAR_RE = /\${([^{}]+)}/g;
38
40
  var NO_SCHEMA_RE = /^\/\/[^/]/;
39
- var WILDCARD_RE = /^(\$?((?:https?|wss?|tunnel):\/\/)?([^/?]+))/;
41
+ var WILDCARD_RE = /^(\$?((?:[a-z*]+):\/\/)?([^/?]*))/;
40
42
  var RULE_KEY_RE = /^\$\{(\S+)\}$/;
41
43
  var VALUE_KEY_RE = /^\{(\S+)\}$/;
42
44
  var LINE_END_RE = /\n|\r\n|\r/;
@@ -44,10 +46,9 @@ var LOCAL_RULE_RE =
44
46
  /^https?:\/\/local\.(?:whistlejs\.com|wproxy\.org)(:realPort)?(?:\/|\?|$)/;
45
47
  var PATH_RE = /^<.*>$/;
46
48
  var VALUE_RE = /^\(.*\)$/;
47
- var REG_URL_RE = /^((?:[\w.*-]+:|\*+)?\/\/)?([^/?]*)/;
48
- var LIKE_REG_URL_RE = /^(?:(?:https?|wss?|tunnel):\/\/)?\*+\/[^?*]*\*/;
49
- var LIKE_REG_URL_RE2 =
50
- /^(?:(?:https?|wss?|tunnel):\/\/)?\.[^./?]+\.[^/?]+\/[^?*]*\*/;
49
+ var REG_URL_RE = /^((?:[a-z*]+:)?\/\/)?([^/?]*)/;
50
+ var LIKE_REG_URL_RE = /^(?:(?:(?:https?|wss?|tunnel):)?\/\/)?\*+\/[^?*]*\*/;
51
+ var LIKE_REG_URL_RE2 = /^(?:(?:(?:https?|wss?|tunnel):)?\/\/)?\.[^./?]+\.[^/?]+\/[^?*]*\*/;
51
52
  var DOT_DOMAIN_RE = /^\.[^./?]+\.[^/?]/;
52
53
  var REG_URL_SYMBOL_RE = /^(\^+)/;
53
54
  var PATTERN_FILTER_RE = /^(?:filter|ignore):\/\/(.+)\/(i)?$/;
@@ -93,13 +94,13 @@ var ENABLE_PROXY_RE =
93
94
  var PLUGIN_VAR_RE = /^%([a-z\d_\-]+)([=.])([^\s]*)/;
94
95
  var EXACT_IGNORE_RE = /^ignore:\/\/(pattern|matcher|operator|operation)[=:](.+)$/;
95
96
  var EXACT_SKIP_RE = /^(pattern|matcher|operator|operation)[=:](.+)$/;
96
- var FILE_RPOTO_RE = /^x?((raw)?file|tpl|jsonp|dust):\/\//;
97
+ var FILE_PROTO_RE = /^x?((raw)?file|tpl|jsonp|dust):\/\//;
97
98
  var NO_PROTO_RE = /[^\w!*|.-]/;
98
99
  var SKIP_RE = /^skip:\/\//;
99
100
  var SUB_VAR_RE = /\$\{RegExp\.\$([&\d])\}/g;
100
101
  var mIndex = 0;
101
102
  var mNow = Date.now();
102
- var IS_JSON = typeof Symbol === 'undefined' ? '_isJson' : Symbol('_isJson'); // eslint-disable-line
103
+ var IS_JSON = Symbol('isJson');
103
104
 
104
105
  function getMFlag() {
105
106
  if (mIndex === Number.MAX_SAFE_INTEGER) {
@@ -181,7 +182,7 @@ function isRegUrl(url, isCheck) {
181
182
  if (!protocol || protocol === '//') {
182
183
  protocol = '[a-z]+://';
183
184
  } else {
184
- protocol = util.escapeRegExp(protocol).replace(/\*+/, '([a-z:]+)');
185
+ protocol = util.escapeRegExp(protocol).replace(/\*+/, '([a-z:]*)');
185
186
  }
186
187
  if (startWithDot) {
187
188
  domain = domain.substring(1);
@@ -290,7 +291,7 @@ function getValue(url, start, end, lineProps) {
290
291
  }
291
292
 
292
293
  function getFiles(path) {
293
- return FILE_RPOTO_RE.test(path) ? util.removeProtocol(path, true).split('|') : null;
294
+ return FILE_PROTO_RE.test(path) ? util.removeProtocol(path, true).split('|') : null;
294
295
  }
295
296
 
296
297
  function setProtocol(target, source) {
@@ -333,7 +334,7 @@ function joinQuery(query1, query2) {
333
334
  : '&';
334
335
  return query1 + sep + query2;
335
336
  }
336
- function join(a, b) {
337
+ function joinUrl(a, b) {
337
338
  if (!a || !b) {
338
339
  return a + b;
339
340
  }
@@ -789,7 +790,7 @@ function getValueFor(key, vals, file) {
789
790
  return;
790
791
  }
791
792
  var key1 = util.getInlineKey(key, file);
792
- var val = vals && vals[key1];
793
+ var val = vals ? vals[key1] : undefined;
793
794
  if (val !== undefined) {
794
795
  val = vals[key1] = val && typeof val == 'object' ? JSON.stringify(val) : val;
795
796
  } else {
@@ -945,13 +946,13 @@ function resolveRuleList(req, list, vals, index, isFilter, isEnableProxy, host)
945
946
  result = extend(
946
947
  {
947
948
  files: files,
948
- url: join(matcher, filePath)
949
+ url: joinUrl(matcher, filePath)
949
950
  },
950
951
  rule
951
952
  );
952
953
  if (files && filePath) {
953
954
  result.files = files.map(function (file) {
954
- return join(file, filePath);
955
+ return joinUrl(file, filePath);
955
956
  });
956
957
  result.rawFiles = files;
957
958
  }
@@ -1027,17 +1028,13 @@ function resolveRuleList(req, list, vals, index, isFilter, isEnableProxy, host)
1027
1028
  }
1028
1029
  } else if (rule.wildcard) {
1029
1030
  var wildcard = rule.wildcard;
1030
- var hostname = null; // 不能去掉
1031
- var regObj;
1032
- if (wildcard.preMatch.test(curUrl)) {
1033
- hostname = RegExp.$1;
1034
- regObj = { 0: hostname };
1035
- for (var k = 1; k < 9; k++) {
1036
- regObj[k] = RegExp['$' + (k + 1)];
1031
+ var matched = wildcard.preMatch.exec(curUrl);
1032
+ if (matched && checkFilter()) {
1033
+ var regObj = {};
1034
+ for (var k = 0; k < 9; k++) {
1035
+ regObj[k] = matched[k + 1] || '';
1037
1036
  }
1038
- }
1039
- if (hostname != null && checkFilter()) {
1040
- filePath = curUrl.substring(hostname.length);
1037
+ filePath = curUrl.substring(regObj[0].length);
1041
1038
  var wPath = wildcard.path;
1042
1039
  if (wildcard.isExact) {
1043
1040
  if (
@@ -1171,6 +1168,7 @@ function parseWildcard(pattern, not) {
1171
1168
  var startWithDot = DOT_DOMAIN_RE.test(domain);
1172
1169
  if (
1173
1170
  !startWithDot &&
1171
+ protocol.indexOf('*') === -1 &&
1174
1172
  domain.indexOf('*') === -1 &&
1175
1173
  domain.indexOf('~') === -1
1176
1174
  ) {
@@ -1179,8 +1177,8 @@ function parseWildcard(pattern, not) {
1179
1177
  if (not) {
1180
1178
  return false;
1181
1179
  }
1182
-
1183
- var path = pattern.substring(preMatch.length) || '/';
1180
+ var restPath = pattern.substring(preMatch.length);
1181
+ var path = restPath || '/';
1184
1182
  var isExact = preMatch.indexOf('$') === 0;
1185
1183
  if (isExact) {
1186
1184
  preMatch = preMatch.substring(1);
@@ -1190,7 +1188,8 @@ function parseWildcard(pattern, not) {
1190
1188
  if (hasQuery && index === 0) {
1191
1189
  path = '/' + path;
1192
1190
  }
1193
- var allowMatchPath = domain.length > 2 && !NON_STAR_RE.test(domain);
1191
+ var dLen = domain.length;
1192
+ var allowMatchPath = dLen > 2 && !NON_STAR_RE.test(domain);
1194
1193
  if (allowMatchPath) {
1195
1194
  preMatch = '[^?]*';
1196
1195
  } else {
@@ -1201,11 +1200,10 @@ function parseWildcard(pattern, not) {
1201
1200
  ) {
1202
1201
  preMatch += '*';
1203
1202
  }
1204
- var dLen = domain.length;
1205
1203
  preMatch = util
1206
1204
  .escapeRegExp(preMatch)
1207
1205
  .replace(DOMAIN_STAR_RE, domainToRegExp);
1208
- if (domain[dLen - 1] !== '*' && domain.indexOf(':') === -1) {
1206
+ if (dLen && domain[dLen - 1] !== '*' && domain.indexOf(':') === -1) {
1209
1207
  preMatch += '(?::\\d+)?';
1210
1208
  }
1211
1209
  if (startWithDot) {
@@ -1218,7 +1216,7 @@ function parseWildcard(pattern, not) {
1218
1216
  preMatch = '[a-z]+:' + preMatch;
1219
1217
  }
1220
1218
  preMatch =
1221
- '^(' + preMatch + ')' + (allowMatchPath ? util.escapeRegExp(path, true) : '');
1219
+ '^(' + preMatch + (restPath ? '' : '[^/?]*') + ')' + (allowMatchPath ? util.escapeRegExp(path, true) : '');
1222
1220
 
1223
1221
  return {
1224
1222
  preMatch: new RegExp(preMatch),
@@ -1411,9 +1409,9 @@ function parseRule(rulesMgr, pattern, matcher, raw, root, options, file) {
1411
1409
  function isPattern(item) {
1412
1410
  return (
1413
1411
  PORT_PATTERN_RE.test(item) ||
1414
- NO_SCHEMA_RE.test(item) ||
1415
1412
  isExactPattern(item) ||
1416
- isRegUrl(item, true) ||
1413
+ isRegUrl(item, true) || // 缓存 regUrl
1414
+ NO_SCHEMA_RE.test(item) ||
1417
1415
  isNegativePattern(item) ||
1418
1416
  WEB_PROTOCOL_RE.test(item) ||
1419
1417
  util.isRegExp(item)
@@ -1655,6 +1653,9 @@ function resolveMatchFilter(list) {
1655
1653
  } else {
1656
1654
  var isHeader = propName !== 'env' && propName !== 'chance' && propName !== 'probability';
1657
1655
  var index = value.indexOf('=');
1656
+ if (isHeader && index === -1) {
1657
+ index = value.indexOf(':');
1658
+ }
1658
1659
  var key = index === -1 ? value : value.substring(0, index);
1659
1660
  if (isHeader) {
1660
1661
  key = key.toLowerCase();
@@ -1843,6 +1844,9 @@ function matchFilter(url, filter, req) {
1843
1844
  if (filter.from === 'composer') {
1844
1845
  return filter.not ? !req.fromComposer : req.fromComposer;
1845
1846
  }
1847
+ if (filter.from === 'internalPath') {
1848
+ return filter.not ? !req.fromInternalPath : req.fromInternalPath;
1849
+ }
1846
1850
  if (filter.from === 'sni') {
1847
1851
  return filter.not ? !req.useSNI : req.useSNI;
1848
1852
  }
@@ -2230,8 +2234,12 @@ function resolveRules(req, isReq, isRes) {
2230
2234
  var protos;
2231
2235
  if (req.isPluginReq) {
2232
2236
  protos = req._isProxyReq ? proxyProtocols : pluginProtocols;
2237
+ } else if (isRes) {
2238
+ protos = pureResProtocols;
2239
+ } else if (isReq) {
2240
+ protos = req._resolvedG ? reqProtosWithoutG : reqProtocols;
2233
2241
  } else {
2234
- protos = isRes ? pureResProtocols : isReq ? reqProtocols : protocols;
2242
+ protos = req._resolvedG ? protocolsWithoutG : protocols;
2235
2243
  }
2236
2244
  req._inlineValues = vals;
2237
2245
  protos.forEach(function (name) {
@@ -2377,6 +2385,10 @@ proto.resolvePipe = function (req) {
2377
2385
  return req.isPluginReq ? null : resolveSingleRule.call(this, req, 'pipe');
2378
2386
  };
2379
2387
 
2388
+ proto.getGRuleList = function (req) {
2389
+ return resolveSingleRule.call(this, req, 'G', true);
2390
+ };
2391
+
2380
2392
  proto.resolvePac = function (req) {
2381
2393
  return resolveSingleRule.call(this, req, 'pac');
2382
2394
  };
package/lib/rules/util.js CHANGED
@@ -487,6 +487,36 @@ if (config.persistentCapture && config.isEnableCapture) {
487
487
  setEnableCapture(true);
488
488
  }
489
489
 
490
+ exports.setDisabledCertFile = function(filename, disabled) {
491
+ var len = util.isString(filename) ? filename.length : 0;
492
+ if (!len || len > 100) {
493
+ return;
494
+ }
495
+ var list = propertiesStorage.getProperty('disabledCertFiles');
496
+ if (!Array.isArray(list)) {
497
+ list = [];
498
+ }
499
+ var index = list.indexOf(filename);
500
+ if (disabled) {
501
+ if (index !== -1) {
502
+ list.splice(index, 1);
503
+ }
504
+ list.push(filename);
505
+ if (list.length > 256) {
506
+ list = list.slice(-256);
507
+ }
508
+ propertiesStorage.setProperty('disabledCertFiles', list);
509
+ } else if (index !== -1) {
510
+ list.splice(index, 1);
511
+ propertiesStorage.setProperty('disabledCertFiles', list);
512
+ }
513
+ };
514
+
515
+ exports.isDisabledCertFile = function(filename) {
516
+ var list = propertiesStorage.getProperty('disabledCertFiles');
517
+ return Array.isArray(list) && list.indexOf(filename) !== -1;
518
+ };
519
+
490
520
  exports.properties = {
491
521
  setIPv6Only: function(checked) {
492
522
  checked = !!checked;
@@ -176,7 +176,7 @@ function getReqRaw(options) {
176
176
  function handleWebSocket(options, cb, count) {
177
177
  count = getReqCount(count);
178
178
  if (options.protocol === 'https:' || options.protocol === 'wss:') {
179
- options.headers[config.HTTPS_FIELD] = 1;
179
+ options.headers[common.HTTPS_FIELD] = 1;
180
180
  }
181
181
  var binary = !!options.headers['x-whistle-frame-binary'];
182
182
  delete options.headers['x-whistle-frame-binary'];
@@ -234,7 +234,7 @@ function handleWebSocket(options, cb, count) {
234
234
  function handleHttp(options, cb, count, reqId) {
235
235
  count = getReqCount(count);
236
236
  if (options.protocol === 'https:') {
237
- options.headers[config.HTTPS_FIELD] = 1;
237
+ options.headers[common.HTTPS_FIELD] = 1;
238
238
  }
239
239
  options.protocol = null;
240
240
  options.hostname = null;
@@ -353,14 +353,14 @@ exports.handleRequest = function(req, res) {
353
353
  var method = common.getMethod(req.body.method);
354
354
  var isWebSocket = method === 'WEBSOCKET';
355
355
  delete headers[config.WEBUI_HEAD];
356
- headers[config.REQ_FROM_HEADER] = 'W2COMPOSER';
356
+ headers[config.FROM_COM_HEADER] = '1';
357
357
  if (req.body.enableProxyRules === false) {
358
358
  headers[config.DISABLE_RULES_HEADER] = '1';
359
359
  }
360
360
  headers.host = options.host;
361
361
  options.clientId = clientId;
362
- headers[config.CLIENT_IP_HEAD] = req.headers[config.CLIENT_IP_HEAD] || '127.0.0.1';
363
- headers[config.CLIENT_PORT_HEAD] = common.getClientPort(req, config);
362
+ headers[config.CLIENT_IP_HEADER] = req.headers[config.CLIENT_IP_HEADER] || '127.0.0.1';
363
+ headers[config.CLIENT_PORT_HEADER] = common.getClientPort(req, config);
364
364
  options.method = method;
365
365
 
366
366
  var isConn = common.isConnect(options);
@@ -256,30 +256,16 @@ function parseJSON(str) {
256
256
 
257
257
  exports.parseJSON = parseJSON;
258
258
 
259
- function padding(num) {
260
- return num < 10 ? '0' + num : num;
261
- }
262
-
263
- function paddingMS(ms) {
264
- if (ms > 99) {
265
- return ms;
266
- }
267
- if (ms > 9) {
268
- return '0' + ms;
269
- }
270
- return '00' + ms;
271
- }
272
-
273
259
  function formatDate() {
274
260
  var date = new Date();
275
261
  var result = [];
276
262
  result.push(date.getFullYear());
277
- result.push(padding(date.getMonth() + 1));
278
- result.push(padding(date.getDate()));
279
- result.push(padding(date.getHours()));
280
- result.push(padding(date.getMinutes()));
281
- result.push(padding(date.getSeconds()));
282
- result.push(paddingMS(date.getMilliseconds()));
263
+ result.push(common.padLeft(date.getMonth() + 1));
264
+ result.push(common.padLeft(date.getDate()));
265
+ result.push(common.padLeft(date.getHours()));
266
+ result.push(common.padLeft(date.getMinutes()));
267
+ result.push(common.padLeft(date.getSeconds()));
268
+ result.push(common.padLeft(date.getMilliseconds(), 3));
283
269
  return result.join('');
284
270
  }
285
271
 
@@ -331,7 +317,7 @@ function toISOString(time) {
331
317
  time.toISOString().slice(0, -1) +
332
318
  '0000' +
333
319
  (offet >= 0 ? '+' : '-') +
334
- padding(Math.abs(offet)) +
320
+ common.padLeft(Math.abs(offet)) +
335
321
  ':00'
336
322
  );
337
323
  }