whistle 2.10.10 → 2.10.11

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.
package/lib/tunnel.js CHANGED
@@ -206,10 +206,9 @@ function tunnelProxy(server, proxy, type) {
206
206
  if (!isIPHost || !hasCustomCerts()) {
207
207
  return false;
208
208
  }
209
- reqSocket.useProxifier =
210
- (config.proxifier2 || (config.proxifier && isWebPort)) &&
211
- !disable.proxifier;
212
- return reqSocket.useProxifier;
209
+ var useProxifier = (config.proxifier2 || (config.proxifier && isWebPort)) && !disable.proxifier;
210
+ reqSocket.useProxifier = useProxifier;
211
+ return useProxifier;
213
212
  };
214
213
  var resolvedPlugin;
215
214
  if (isIntercept()) {
@@ -248,14 +247,19 @@ function tunnelProxy(server, proxy, type) {
248
247
  pluginMgr.getTunnelRules(req, function (_rulesMgr) {
249
248
  handlePluginRules(_rulesMgr);
250
249
  policy = headers[config.WHISTLE_POLICY_HEADER];
250
+ var useSniAsTunnelDomain = util.isEnable(req, 'useSniAsTunnelDomain');
251
+ var noCapture;
251
252
  if (!reqSocket._hasError && !req._authForbidden &&
252
- !req.fromComposer && (req._forceCapture || isIntercept())) {
253
+ !req.fromComposer && (req._forceCapture || isIntercept() || (noCapture = useSniAsTunnelDomain))) {
253
254
  reqSocket.rulesHeaders = req.rulesHeaders;
254
255
  reqSocket.enable = req.enable;
255
256
  reqSocket.disable = req.disable;
256
257
  reqSocket.tunnelHostname = hostname;
258
+ reqSocket.tunnelPort = options.port;
257
259
  reqSocket.rules = _rules;
258
260
  reqSocket._pluginVars = req._pluginVars;
261
+ reqSocket._noCaptureW2 = noCapture;
262
+ reqSocket._useSniAsTunnelDomain = useSniAsTunnelDomain;
259
263
  dispatch(
260
264
  reqSocket,
261
265
  function (chunk) {
@@ -74,6 +74,13 @@ var PROP_SEP_RE = /(\\*)([|&]|\\[stnrfv])/g;
74
74
  var TEMP_FILE_RE = /^(?:\/?temp\/)?([\da-f]{64})(?:\.[\w.-]+)?$/;
75
75
  var TEMP_FILES_PATH = path.join(getWhistlePath(), 'temp_files');
76
76
  var SAVED_SESSIONS_FILE_RE = /_([1-9]\d*)_(\d+)$/;
77
+ var REG_EXP_RE = /^\/(.+)\/(i?u?|ui)$/;
78
+
79
+ function isRegExp(regExp) {
80
+ return REG_EXP_RE.test(regExp);
81
+ }
82
+
83
+ exports.isRegExp = isRegExp;
77
84
 
78
85
  exports.getSavedFileItem = function(filename) {
79
86
  var result = SAVED_SESSIONS_FILE_RE.exec(filename);
@@ -1905,16 +1912,14 @@ exports.connectInner = function (options, cb, config) {
1905
1912
  auth = 'Basic ' + auth.toString('base64');
1906
1913
  setRawHeader(headers, 'proxy-authorization', auth);
1907
1914
  }
1915
+ var req;
1908
1916
  var timer = setTimeout(function () {
1909
- if (req) {
1910
- req.emit('error', TIMEOUT_ERR);
1911
- req.destroy();
1912
- }
1917
+ req.emit('error', TIMEOUT_ERR);
1918
+ req.destroy();
1913
1919
  }, CONN_TIMEOUT);
1914
- var req;
1915
1920
  var clearTimer = function (err) {
1916
1921
  if (timer) {
1917
- err && req && req.destroy();
1922
+ err && req.destroy();
1918
1923
  clearTimeout(timer);
1919
1924
  timer = null;
1920
1925
  }
@@ -1929,14 +1934,15 @@ exports.connectInner = function (options, cb, config) {
1929
1934
  socket.on('error', function() {
1930
1935
  socket.destroy();
1931
1936
  });
1932
- if (res.statusCode !== 200) {
1933
- var err = new Error(
1934
- 'Tunneling socket could not be established, statusCode=' +
1935
- res.statusCode
1936
- );
1937
- err.statusCode = res.statusCode;
1937
+ var status = res.statusCode;
1938
+ if (status !== 200) {
1938
1939
  socket.destroy();
1939
1940
  process.nextTick(function () {
1941
+ var err = new Error(
1942
+ 'Tunneling socket could not be established, statusCode=' +
1943
+ status
1944
+ );
1945
+ err.statusCode = status;
1940
1946
  req.emit('error', err);
1941
1947
  });
1942
1948
  return;
@@ -2135,3 +2141,127 @@ function getHttpMeta(f1, f2, f3, headers, rawHeaders) {
2135
2141
  }
2136
2142
 
2137
2143
  exports.getHttpMeta = getHttpMeta;
2144
+
2145
+
2146
+ var PATTERN_FIELDS = ['url', 'm', 'method', 'reqH', 'reqHeader', 'reqHeaders',
2147
+ 'b', 'body', 'reqBody', 's', 'statusCode', 'status', 'resH', 'resHeader', 'resHeaders'];
2148
+
2149
+ function getHeaderKey(key) {
2150
+ return !key || typeof key !== 'string' || /[^\w!#$%&'*+.^`|~-]/.test(key) ? '' : key.toLowerCase();
2151
+ }
2152
+
2153
+ function createFilter(item, name, key) {
2154
+ var protocol = (item.exclude ? 'ex' : 'in') + 'cludeFilter://';
2155
+ name = name ? name + (key ? '.' : ':') : '';
2156
+ key = key ? key + ':' : '';
2157
+ return protocol + name + key + item.value;
2158
+ }
2159
+
2160
+ function wrapRegexp(value, item) {
2161
+ var flags = item.ignoreCase || item.matchCase === false ? 'i' : '';
2162
+ return { value: '/' + value.replace(/\//g, '\\$&') + '/' + flags, exclude: item.exclude};
2163
+ }
2164
+
2165
+ function formatPath(url) {
2166
+ if (/^\^?\/\//.test(url)) {
2167
+ return url;
2168
+ }
2169
+ return url.replace(/^(\^?(?:https?:\/\/|wss?:\/\/)?)([/?.])/, function(all, prefix, flag) {
2170
+ return flag === '/' ? prefix + '**/' : (prefix[0] === '^' ? prefix : '^' + prefix) + '**/**' + flag;
2171
+ });
2172
+ }
2173
+
2174
+ function formatUrlPattern(item) {
2175
+ var value = item.value;
2176
+ var type = item.type;
2177
+ var prefix;
2178
+ if (/^\s*(keyword|contains?)\s*$/i.test(type)) {
2179
+ return wrapRegexp(item.value.replace(/[|/\\{}()[\]^$+?*.]/g, '\\$&'), item);
2180
+ }
2181
+ if (/^\s*regexp?\s*$/i.test(type)) {
2182
+ return wrapRegexp(item.value.replace(/\//g, '\\$&'), item);
2183
+ }
2184
+ if (/^\s*(wildcard|star)\s*$/i.test(type)) {
2185
+ return { value: formatPath(item.value.replace(/^\^*/, '^')), exclude: item.exclude };
2186
+ }
2187
+ if (value[0] === '?') {
2188
+ prefix = '^**/**';
2189
+ } else if (value[0] !== '/' || value[1] === '/' || isRegExp(value)) {
2190
+ return { value: formatPath(value) };
2191
+ }
2192
+ return { value: (prefix || '**') + value };
2193
+ }
2194
+
2195
+ function createPattern(list) {
2196
+ if (!list) {
2197
+ return '';
2198
+ }
2199
+ if (!Array.isArray(list)) {
2200
+ list = [list];
2201
+ }
2202
+ var len = list.length;
2203
+ if (!len) {
2204
+ return '';
2205
+ }
2206
+ var result = [];
2207
+ var urlPattern;
2208
+ var key;
2209
+ var add = function(item, name, key) {
2210
+ var p = createFilter(item, name, key);
2211
+ if (result.indexOf(p) === -1) {
2212
+ result.push(p);
2213
+ }
2214
+ };
2215
+ list.forEach(function(item) {
2216
+ if (!item || !item.value) {
2217
+ return;
2218
+ }
2219
+ var name = item.name || 'url';
2220
+ if (PATTERN_FIELDS.indexOf(name) === -1) {
2221
+ return;
2222
+ }
2223
+ if (typeof item.value !== 'string') {
2224
+ item.value = String(item.value);
2225
+ }
2226
+ if (/\s/.test(item.value)) {
2227
+ return;
2228
+ }
2229
+ switch(name) {
2230
+ case 'url':
2231
+ item = formatUrlPattern(item);
2232
+ if (urlPattern || item.exclude) {
2233
+ add(item);
2234
+ } else {
2235
+ urlPattern = item.value;
2236
+ }
2237
+ return;
2238
+ case 'm':
2239
+ case 'method':
2240
+ return add(item, 'm');
2241
+ case 'reqH':
2242
+ case 'reqHeader':
2243
+ case 'reqHeaders':
2244
+ key = getHeaderKey(item.key);
2245
+ return key && add(item, 'reqH', key);
2246
+ case 'b':
2247
+ case 'body':
2248
+ case 'reqBody':
2249
+ return add(item, 'b');
2250
+ case 's':
2251
+ case 'statusCode':
2252
+ case 'status':
2253
+ return add(item, 's');
2254
+ case 'resH':
2255
+ case 'resHeader':
2256
+ case 'resHeaders':
2257
+ key = getHeaderKey(item.key);
2258
+ return key && add(item, 'resH', key);
2259
+ }
2260
+ });
2261
+ if (urlPattern || result.length) {
2262
+ result.unshift(urlPattern || '*');
2263
+ }
2264
+ return result.join(' ');
2265
+ }
2266
+
2267
+ exports.createPattern = createPattern;
package/lib/util/index.js CHANGED
@@ -24,7 +24,7 @@ var ReplacePatternTransform = require('./replace-pattern-transform');
24
24
  var common = require('./common');
25
25
  var proc = require('./process');
26
26
  var parseUrl = require('./parse-url');
27
- var h2Consts = config.enableH2 ? require('http2').constants : {};
27
+ var h2Consts = require('http2').constants;
28
28
 
29
29
  var protocols = protoMgr.protocols;
30
30
  var resProtocols = protoMgr.resProtocols;
@@ -39,7 +39,6 @@ var pendingFiles = {};
39
39
  var localIpCache = new LRU({ max: 120 });
40
40
  var LOCALHOST = '127.0.0.1';
41
41
  var aliasProtocols = protoMgr.aliasProtocols;
42
- var CONTEXT = vm.createContext();
43
42
  var END_SLASH_RE = /[/\\]$/;
44
43
  var GEN_URL_RE = /^\s*(?:https?:)?\/\/\w[^\s]*\s*$/i;
45
44
  var CORS_KEY_RE = /^(?:enable|use-credentials|useCredentials|credentials)$/i;
@@ -50,17 +49,12 @@ var STYLE_START = toBuffer('<style>');
50
49
  var STYLE_END = toBuffer('</style>');
51
50
  var PROXY_RE = /^x?(?:socks|https?-proxy|proxy|internal(?:-https)?-proxy)$/;
52
51
  var SEP_RE = /[|&]/;
53
- var ctxTimer;
54
52
  var END_RE = /[/\\]$/;
55
53
  var EXPIRED_SEC = -123456;
56
54
  var EXP_COOKIE = { maxAge: EXPIRED_SEC, path: '/' };
57
55
  var EXP_SECURE_COOKIE = { maxAge: EXPIRED_SEC, path: '/', secure: true };
58
56
  var EXP_COOKIE_D = { maxAge: EXPIRED_SEC, path: '/' };
59
57
  var EXP_SECURE_COOKIE_D = { maxAge: EXPIRED_SEC, path: '/', secure: true };
60
- var resetContext = function () {
61
- ctxTimer = null;
62
- CONTEXT = vm.createContext();
63
- };
64
58
  var TIMEOUT_ERR = common.TIMEOUT_ERR;
65
59
  var SUB_MATCH_RE = /\$[&\d]/;
66
60
  var PROTO_NAME_RE = /^([\w.-]+):\/\//;
@@ -89,6 +83,7 @@ var getMatcher = common.getMatcher;
89
83
  var getMatcherValue = common.getMatcherValue;
90
84
  var getRemoteAddr = common.getRemoteAddr;
91
85
  var getRemotePort = common.getRemotePort;
86
+ var isRegExp = common.isRegExp;
92
87
  var workerIndex = process.env && process.env.workerIndex;
93
88
  var INTERNAL_ID = Date.now().toString(16) + Math.floor(Math.random() * 10000000).toString(16);
94
89
  var pluginMgr;
@@ -175,6 +170,7 @@ exports.PLUGIN_INSPECTOR_CONFIG = Symbol('inspectorConfig');
175
170
  exports.drain = require('./drain');
176
171
  exports.isWin = process.platform === 'win32';
177
172
  exports.isUtf8 = isUtf8;
173
+ exports.isRegExp = isRegExp;
178
174
  exports.WhistleTransform = require('./whistle-transform');
179
175
  exports.ReplacePatternTransform = ReplacePatternTransform;
180
176
  exports.replacePattern = replacePattern;
@@ -425,7 +421,7 @@ exports.compare = compare;
425
421
  var scriptCache = {};
426
422
  var VM_OPTIONS = {
427
423
  displayErrors: false,
428
- timeout: 60
424
+ timeout: 72
429
425
  };
430
426
  var MAX_SCRIPT_SIZE = 1024 * 256;
431
427
  var MAX_SCRIPT_CACHE_COUNT = 64;
@@ -471,34 +467,20 @@ function getScript(content) {
471
467
  return script.script;
472
468
  }
473
469
 
474
- function clearContext() {
475
- Object.keys(CONTEXT).forEach(function (key) {
476
- delete CONTEXT[key];
477
- });
478
- if (!ctxTimer) {
479
- ctxTimer = setTimeout(resetContext, 30000);
480
- }
481
- }
470
+ var customConsole = {};
471
+ ['fatal', 'error', 'warn', 'info', 'log', 'debug'].forEach(function (level) {
472
+ customConsole[level] = logger[level];
473
+ });
482
474
 
483
- exports.execScriptSync = function(script, context) {
475
+ exports.runInContext = function(script, context) {
484
476
  try {
485
- if ((script = getScript(script))) {
486
- CONTEXT.console = {};
487
- ['fatal', 'error', 'warn', 'info', 'log', 'debug'].forEach(function (
488
- level
489
- ) {
490
- CONTEXT.console[level] = logger[level];
491
- });
492
- Object.keys(context).forEach(function (key) {
493
- CONTEXT[key] = context[key];
494
- });
495
- script.runInContext(CONTEXT, VM_OPTIONS);
477
+ if (script = getScript(script)) {
478
+ context = Object.assign({ console: Object.create(customConsole) }, context);
479
+ script.runInNewContext(context, VM_OPTIONS);
496
480
  }
497
481
  return true;
498
482
  } catch (e) {
499
483
  logger.error(e);
500
- } finally {
501
- clearContext();
502
484
  }
503
485
  };
504
486
 
@@ -603,14 +585,6 @@ function formatDate(now) {
603
585
 
604
586
  exports.formatDate = formatDate;
605
587
 
606
- var REG_EXP_RE = /^\/(.+)\/(i?u?|ui)$/;
607
-
608
- function isRegExp(regExp) {
609
- return REG_EXP_RE.test(regExp);
610
- }
611
-
612
- exports.isRegExp = isRegExp;
613
-
614
588
  var ORIG_REG_EXP = /^\/(.+)\/([igmu]{0,4})$/;
615
589
  var FLAGS_RE = /[igmu]{2}/;
616
590
 
@@ -723,14 +697,13 @@ function isAuthCapture(req) {
723
697
 
724
698
  exports.isAuthCapture = isAuthCapture;
725
699
 
726
- exports.toRegExp = function toRegExp(regExp, ignoreCase) {
727
- regExp = REG_EXP_RE.test(regExp);
728
- try {
729
- regExp = regExp && new RegExp(RegExp.$1, ignoreCase ? 'i' : RegExp.$2);
730
- } catch (e) {
731
- regExp = null;
700
+ exports.toRegExp = function(regExp, ignoreCase) {
701
+ if (isRegExp(regExp)) {
702
+ try {
703
+ return new RegExp(RegExp.$1, ignoreCase ? 'i' : RegExp.$2);
704
+ } catch (e) {}
732
705
  }
733
- return regExp;
706
+ return null;
734
707
  };
735
708
 
736
709
  var isString = common.isString;
@@ -2983,7 +2956,7 @@ exports.disableReqProps = function (req) {
2983
2956
  delete headers['user-agent'];
2984
2957
  }
2985
2958
 
2986
- if (disable.gzip || isEnable(req, 'captureStream')) {
2959
+ if (disable.gzip || disable.zip || isEnable(req, 'captureStream')) {
2987
2960
  delete headers['accept-encoding'];
2988
2961
  }
2989
2962
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "whistle",
3
3
  "description": "HTTP, HTTP2, HTTPS, Websocket debugging proxy",
4
- "version": "2.10.10",
4
+ "version": "2.10.11",
5
5
  "dataDirname": ".whistle",
6
6
  "localUIHost": "local.whistlejs.com",
7
7
  "port": 8899,
@@ -37,7 +37,7 @@
37
37
  "url": "https://github.com/avwo/whistle.git"
38
38
  },
39
39
  "dependencies": {
40
- "adm-zip": "^0.6.0",
40
+ "adm-zip": "^0.6.1",
41
41
  "async-limiter": "2.0.0",
42
42
  "body-parser": "^1.20.6",
43
43
  "colors": "1.1.2",
@@ -75,7 +75,7 @@
75
75
  "docs": "vitepress build docs"
76
76
  },
77
77
  "engines": {
78
- "node": ">= 14.0.0"
78
+ "node": ">= 16.0.0"
79
79
  },
80
80
  "devDependencies": {
81
81
  "babel-core": "^6.7.6",