whistle 2.10.3 → 2.10.5

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 (42) hide show
  1. package/assets/menu.html +0 -1
  2. package/assets/modal.html +0 -1
  3. package/assets/tab.html +0 -1
  4. package/bin/proxy.js +2 -2
  5. package/biz/webui/cgi-bin/add-rules-values.js +1 -1
  6. package/biz/webui/cgi-bin/check-update.js +0 -1
  7. package/biz/webui/cgi-bin/do-not-show-again.js +1 -1
  8. package/biz/webui/cgi-bin/enable-http2.js +1 -1
  9. package/biz/webui/cgi-bin/get-matched-rules.js +14 -0
  10. package/biz/webui/cgi-bin/hide-https-connects.js +1 -1
  11. package/biz/webui/cgi-bin/intercept-https-connects.js +1 -1
  12. package/biz/webui/cgi-bin/plugins/disable-all-plugins.js +1 -1
  13. package/biz/webui/cgi-bin/reset-local-address.js +1 -1
  14. package/biz/webui/cgi-bin/rules/allow-multiple-choice.js +1 -1
  15. package/biz/webui/cgi-bin/rules/clear-dns-cache.js +1 -1
  16. package/biz/webui/cgi-bin/rules/disable-all-rules.js +1 -1
  17. package/biz/webui/cgi-bin/rules/disable-default.js +1 -1
  18. package/biz/webui/cgi-bin/rules/enable-back-rules-first.js +1 -1
  19. package/biz/webui/cgi-bin/rules/enable-default.js +1 -1
  20. package/biz/webui/cgi-bin/rules/move-to.js +1 -1
  21. package/biz/webui/cgi-bin/rules/remove.js +1 -1
  22. package/biz/webui/cgi-bin/rules/rename.js +1 -1
  23. package/biz/webui/cgi-bin/rules/select.js +1 -1
  24. package/biz/webui/cgi-bin/rules/set-sys-hosts.js +1 -1
  25. package/biz/webui/cgi-bin/rules/unselect.js +1 -1
  26. package/biz/webui/cgi-bin/util.js +0 -1
  27. package/biz/webui/cgi-bin/values/move-to.js +1 -1
  28. package/biz/webui/cgi-bin/values/remove.js +1 -1
  29. package/biz/webui/cgi-bin/values/rename.js +1 -1
  30. package/biz/webui/htdocs/index.html +1 -1
  31. package/biz/webui/htdocs/js/index.js +52 -54
  32. package/lib/config.js +1 -0
  33. package/lib/https/index.js +1 -0
  34. package/lib/inspectors/data.js +2 -1
  35. package/lib/rules/rules.js +5 -5
  36. package/lib/service/composer.js +77 -35
  37. package/lib/tunnel.js +3 -0
  38. package/lib/upgrade.js +1 -0
  39. package/lib/util/common.js +11 -1
  40. package/lib/util/data-server.js +10 -0
  41. package/lib/util/index.js +14 -15
  42. package/package.json +2 -2
package/lib/config.js CHANGED
@@ -91,6 +91,7 @@ config.WEBUI_HEAD = 'x-forwarded-from-whistle-' + uid;
91
91
  config.RES_RULES_HEAD = 'x-whistle-res-rules-' + uid;
92
92
  config.COMPOSER_CLIENT_ID_HEADER = 'x-whistle-client-id-' + uid;
93
93
  config.FROM_COM_HEADER = 'x-whistle-composer-' + uid;
94
+ config.TEST_HEADER = 'x-whistle-test-' + uid;
94
95
  config.PLUGIN_HOOKS = {
95
96
  SNI: 'sni-' + uid,
96
97
  AUTH: 'auth-' + uid,
@@ -106,6 +106,7 @@ function resolveWebsocket(socket, wss) {
106
106
  _clientId: socket._clientId,
107
107
  id: socket.reqId,
108
108
  fc: socket.fromComposer ? 1 : undefined,
109
+ testId: socket._testId,
109
110
  url: fullUrl,
110
111
  startTime: now,
111
112
  sniPlugin: socket.sniPlugin,
@@ -158,6 +158,7 @@ function emitDataEvents(req, res, proxy) {
158
158
  h2Id: req._alpn,
159
159
  fc: req.fromComposer ? 1 : undefined,
160
160
  isPR: req.isPluginReq ? 1 : undefined,
161
+ testId: req._testId,
161
162
  _clientId: req._clientId,
162
163
  startTime: now,
163
164
  ttfb: getTTFB(),
@@ -417,7 +418,7 @@ function emitDataEvents(req, res, proxy) {
417
418
  req.once('dest', function (_req) {
418
419
  _req.once('finish', function () {
419
420
  if (!requestTime) {
420
- setRequestTime(requestTime);
421
+ setRequestTime(Date.now());
421
422
  }
422
423
  });
423
424
  setReqStatus();
@@ -7,6 +7,7 @@ var rulesUtil = require('./util');
7
7
  var lookup = require('./dns');
8
8
  var protoMgr = require('./protocols');
9
9
  var config = require('../config');
10
+ var common = require('../util/common');
10
11
 
11
12
  var rules = rulesUtil.rules;
12
13
  var values = rulesUtil.values;
@@ -68,7 +69,6 @@ var NON_STAR_RE = /[^*]/;
68
69
  var DOMAIN_STAR_RE = /([*~]+)(\\.)?/g;
69
70
  var STAR_RE = /\*+/g;
70
71
  var PORT_PATTERN_RE = /^!?:\d{1,5}$/;
71
- var COMMENT_RE = /#[^\r\n]*/g;
72
72
  var TPL_RE = /^((?:[\w.-]+:)?\/\/)?(`.*`)$/;
73
73
  // url: protocol, host, port, hostname, search, query, pathname, path, href, query.key
74
74
  // req|res: ip, method, statusCode, headers?.key, cookies?.key
@@ -101,6 +101,7 @@ var TOOL_RE = /^(?:log|weinre):\/\//;
101
101
  var mIndex = 0;
102
102
  var mNow = Date.now();
103
103
  var IS_JSON = Symbol('isJson');
104
+ var removeComment = common.removeComment;
104
105
 
105
106
  function getMFlag() {
106
107
  if (mIndex === Number.MAX_SAFE_INTEGER) {
@@ -110,10 +111,6 @@ function getMFlag() {
110
111
  return mNow + '-' + (mIndex++);
111
112
  }
112
113
 
113
- function removeComment(text) {
114
- return text.replace(COMMENT_RE, '').trim();
115
- }
116
-
117
114
  function domainToRegExp(all, star, dot) {
118
115
  var len = star.length;
119
116
  var result = len > 1 ? '([^/?]*)' : '([^/?.]*)';
@@ -1838,6 +1835,9 @@ function matchFilter(url, filter, req) {
1838
1835
  if (filter.from === 'tunnel') {
1839
1836
  return filter.not ? !req.fromTunnel : req.fromTunnel;
1840
1837
  }
1838
+ if (filter.from === 'test') {
1839
+ return filter.not ? !req._testId : req._testId;
1840
+ }
1841
1841
  if (filter.from === 'composer') {
1842
1842
  return filter.not ? !req.fromComposer : req.fromComposer;
1843
1843
  }
@@ -29,6 +29,7 @@ var MAX_BODY_LEN = 260 * 1024;
29
29
  var MAX_BASE64_LEN = 360 * 1024;
30
30
  var MAX_METHOD_LEN = 64;
31
31
  var LEN_HEADER = 'content-length';
32
+ var testIndex = 0;
32
33
 
33
34
  var composerTimer;
34
35
  function saveComposerHistory() {
@@ -125,7 +126,24 @@ function getReqCount(count) {
125
126
  return count > 0 ? Math.min(count, MAX_REQ_COUNT) : 1;
126
127
  }
127
128
 
128
- function handleConnect(options, cb, count) {
129
+ function getResponse(res, testId) {
130
+ if (testId) {
131
+ return { testId: testId };
132
+ }
133
+ return { statusCode: res.statusCode, headers: res.headers || {} };
134
+ }
135
+
136
+ function handleCallback(cb, err, res, testId) {
137
+ if (!cb) {
138
+ return;
139
+ }
140
+ if (testId) {
141
+ return cb(null, getResponse(null, testId));
142
+ }
143
+ cb(err, !err && getResponse(res));
144
+ }
145
+
146
+ function handleConnect(options, cb, count, testId) {
129
147
  count = getReqCount(count);
130
148
  options.headers['x-whistle-policy'] = 'tunnel';
131
149
  var origOpts = options;
@@ -145,7 +163,7 @@ function handleConnect(options, cb, count) {
145
163
  headers: options.headers
146
164
  }, function(socket, svrRes, err) {
147
165
  if (err) {
148
- return execCb && execCb(err);
166
+ return handleCallback(execCb, err, null, testId);
149
167
  }
150
168
  if (TLS_PROTOS.indexOf(options.protocol) !== -1) {
151
169
  socket = tls.connect({
@@ -160,10 +178,7 @@ function handleConnect(options, cb, count) {
160
178
  socket.write(data);
161
179
  options.body = data = null;
162
180
  }
163
- execCb && execCb(null, {
164
- statusCode: svrRes.statusCode,
165
- headers: svrRes.headers
166
- });
181
+ handleCallback(execCb, err, svrRes, testId);
167
182
  }, config).on('error', execCb || noop);
168
183
  }
169
184
  }
@@ -178,7 +193,7 @@ function getReqRaw(options) {
178
193
  return raw + '\r\n\r\n';
179
194
  }
180
195
 
181
- function handleWebSocket(options, cb, count) {
196
+ function handleWebSocket(options, cb, count, testId) {
182
197
  count = getReqCount(count);
183
198
  if (options.protocol === 'https:' || options.protocol === 'wss:') {
184
199
  options.headers[common.HTTPS_FIELD] = 1;
@@ -196,7 +211,7 @@ function handleWebSocket(options, cb, count) {
196
211
  }
197
212
  common.connect(PROXY_OPTS, function(err, socket) {
198
213
  if (err) {
199
- execCb && execCb(err);
214
+ handleCallback(execCb, err, null, testId);
200
215
  } else {
201
216
  socket.write(getReqRaw(options));
202
217
  var data = options.body;
@@ -206,7 +221,7 @@ function handleWebSocket(options, cb, count) {
206
221
  parseReq(socket, function(e) {
207
222
  if (e) {
208
223
  socket.destroy();
209
- return execCb && execCb(e);
224
+ return handleCallback(execCb, e, null, testId);
210
225
  }
211
226
  var statusCode = socket.statusCode;
212
227
  if (statusCode == 101) {
@@ -226,17 +241,14 @@ function handleWebSocket(options, cb, count) {
226
241
  } else {
227
242
  socket.destroy();
228
243
  }
229
- execCb && execCb(null, {
230
- statusCode: statusCode,
231
- headers: socket.headers || {}
232
- });
244
+ handleCallback(execCb, err, socket, testId);
233
245
  }, true);
234
246
  }
235
247
  });
236
248
  }
237
249
  }
238
250
 
239
- function handleHttp(options, cb, count, reqId) {
251
+ function handleHttp(options, cb, count, reqId, testId) {
240
252
  count = getReqCount(count);
241
253
  if (options.protocol === 'https:') {
242
254
  options.headers[common.HTTPS_FIELD] = 1;
@@ -255,7 +267,8 @@ function handleHttp(options, cb, count, reqId) {
255
267
  options = extend({}, origOpts);
256
268
  }
257
269
  var client = http.request(options, function(svrRes) {
258
- if (!execCb) {
270
+ if (!execCb || testId) {
271
+ handleCallback(execCb, null, svrRes, testId);
259
272
  return drain(svrRes);
260
273
  }
261
274
  svrRes.on('error', execCb);
@@ -338,7 +351,8 @@ function getCharset(headers) {
338
351
  }
339
352
 
340
353
  exports.handleRequest = function(req, res) {
341
- var fullUrl = req.body.url;
354
+ var reqBody = req.body;
355
+ var fullUrl = reqBody.url;
342
356
  if (!fullUrl || typeof fullUrl !== 'string') {
343
357
  return res.json({ec: 2, em: 'Invalid URL'});
344
358
  }
@@ -354,13 +368,41 @@ exports.handleRequest = function(req, res) {
354
368
  }
355
369
  var rawHeaderNames = {};
356
370
  var clientId = req.headers[config.CLIENT_ID_HEADER];
357
- var headers = parseHeaders(req.body.headers, rawHeaderNames, clientId);
358
- var method = common.getMethod(req.body.method);
371
+ var headers = parseHeaders(reqBody.headers, rawHeaderNames, clientId);
372
+ var method = common.getMethod(reqBody.method);
359
373
  var isWebSocket = method === 'WEBSOCKET';
374
+ var rules = common.trimStr(reqBody.rules);
375
+ if (rules) {
376
+ var curRules = headers['x-whistle-rule-value'];
377
+ if (Array.isArray(curRules)) {
378
+ curRules = curRules.join('\n');
379
+ }
380
+ curRules = common.trimStr(curRules);
381
+ if (curRules) {
382
+ try {
383
+ curRules = decodeURIComponent(curRules);
384
+ rules = common.removeComment(rules + '\n' + curRules);
385
+ } catch (e) {}
386
+ }
387
+ headers['x-whistle-rule-value'] = common.safeEncodeURIComponent(rules);
388
+ }
389
+ var type = common.trimStr(reqBody.contentType);
390
+ if (type) {
391
+ headers['content-type'] = type;
392
+ }
393
+ if (reqBody.contentLength >= 0) {
394
+ headers['content-length'] = reqBody.contentLength;
395
+ }
360
396
  delete headers[config.WEBUI_HEAD];
361
397
  headers[config.FROM_COM_HEADER] = '1';
362
- if (req.body.enableProxyRules === false) {
398
+ var isTest = reqBody.isTest;
399
+ var testId;
400
+ if (reqBody.enableProxyRules === false || isTest) {
363
401
  headers[config.DISABLE_RULES_HEADER] = '1';
402
+ if (isTest) {
403
+ testId = ++testIndex + '';
404
+ headers[config.TEST_HEADER] = testId;
405
+ }
364
406
  }
365
407
  headers.host = options.host;
366
408
  options.clientId = clientId;
@@ -370,8 +412,8 @@ exports.handleRequest = function(req, res) {
370
412
 
371
413
  var isConn = common.isConnect(options);
372
414
  var isWs = !isConn && (isWebSocket || common.isUpgrade(options, headers));
373
- var useH2 = req.body.useH2 || req.body.isH2;
374
- req.body.useH2 = false;
415
+ var useH2 = reqBody.useH2 || reqBody.isH2;
416
+ reqBody.useH2 = false;
375
417
  if (isWs) {
376
418
  headers.connection = 'Upgrade';
377
419
  headers.upgrade = (!isWebSocket && headers.upgrade) || 'websocket';
@@ -382,7 +424,7 @@ exports.handleRequest = function(req, res) {
382
424
  } else {
383
425
  delete headers.upgrade;
384
426
  if (!isConn && ((useH2 && (protocol === 'https:' || protocol === 'http:')) || protocol === 'h2:' || protocol === 'http2:')) {
385
- req.body.useH2 = true;
427
+ reqBody.useH2 = true;
386
428
  var isHttp = protocol === 'http:';
387
429
  options.protocol = isHttp ? 'http:' : 'https:';
388
430
  if (!headers[config.ALPN_PROTOCOL_HEADER]) {
@@ -390,8 +432,8 @@ exports.handleRequest = function(req, res) {
390
432
  }
391
433
  }
392
434
  }
393
- if (!req.body.noStore && req.body.needResponse && common.checkHistory(req.body)) {
394
- handleComposerHistory(req.body, isConn);
435
+ if (!reqBody.noStore && !testId && reqBody.needResponse && common.checkHistory(reqBody)) {
436
+ handleComposerHistory(reqBody, isConn);
395
437
  dataCenter.saveData({
396
438
  type: 'composer',
397
439
  history: composerHistory
@@ -399,15 +441,15 @@ exports.handleRequest = function(req, res) {
399
441
  }
400
442
 
401
443
  var getBody = function(cb) {
402
- var base64 = req.body.base64;
403
- var body = base64 || req.body.body;
444
+ var base64 = reqBody.base64;
445
+ var body = base64 || reqBody.body;
404
446
  if (!isWs) {
405
447
  delete headers.trailer;
406
448
  }
407
449
  if (isWs || isConn || common.hasRequestBody(options)) {
408
450
  body = body && common.toBuffer(body, base64 ? 'base64' : getCharset(headers));
409
451
  options.body = body;
410
- if (!isWs && !isConn && body && req.body.isGzip) {
452
+ if (!isWs && !isConn && body && reqBody.isGzip) {
411
453
  gzip(body, function(err, gzipData) {
412
454
  if (err) {
413
455
  return cb(err);
@@ -439,7 +481,7 @@ exports.handleRequest = function(req, res) {
439
481
  getBody(function(err) {
440
482
  options.headers = formatHeaders(headers, rawHeaderNames);
441
483
  var done;
442
- var needResponse = req.query.needResponse || req.body.needResponse;
484
+ var needResponse = req.query.needResponse || reqBody.needResponse;
443
485
  var handleResponse = needResponse ? function(err, data) {
444
486
  if (done) {
445
487
  return;
@@ -453,23 +495,23 @@ exports.handleRequest = function(req, res) {
453
495
  }});
454
496
  return;
455
497
  }
456
- common.sendGzip(req, res, {ec: 0, em: 'success', res: data || ''});
498
+ common.sendGzip(req, res, {ec: 0, res: data || ''});
457
499
  } : null;
458
500
  if (err) {
459
501
  return handleResponse && handleResponse(err);
460
502
  }
461
- var count = req.body.repeatCount;
462
- count = count > 0 ? count : req.body.repeatTimes;
503
+ var count = reqBody.repeatCount;
504
+ count = count > 0 ? count : reqBody.repeatTimes;
463
505
  if (isWs) {
464
506
  options.method = 'GET';
465
- handleWebSocket(options, handleResponse, count);
507
+ handleWebSocket(options, handleResponse, count, testId);
466
508
  } else if (isConn) {
467
- handleConnect(options, handleResponse, count);
509
+ handleConnect(options, handleResponse, count, testId);
468
510
  } else {
469
- handleHttp(options, handleResponse, count, req.body.reqId);
511
+ handleHttp(options, handleResponse, count, reqBody.reqId, testId);
470
512
  }
471
513
  if (!handleResponse) {
472
- res.json({ec: 0, em: 'success'});
514
+ res.json({ec: 0});
473
515
  }
474
516
  });
475
517
  };
package/lib/tunnel.js CHANGED
@@ -91,6 +91,7 @@ function tunnelProxy(server, proxy, type) {
91
91
  reqSocket.headers = headers;
92
92
  reqSocket.fromTunnel = req.fromTunnel;
93
93
  reqSocket.fromComposer = req.fromComposer;
94
+ reqSocket._testId = req._testId;
94
95
  req.isPluginReq = reqSocket.isPluginReq = util.checkPluginReqOnce(req, true);
95
96
  util.onSocketEnd(reqSocket, function (err) {
96
97
  // 处理可能的证书校验出错
@@ -100,6 +101,7 @@ function tunnelProxy(server, proxy, type) {
100
101
  id: req.reqId,
101
102
  url: options.host,
102
103
  fc: req.fromComposer ? 1 : undefined,
104
+ testId: req._testId,
103
105
  startTime: now,
104
106
  dnsTime: now,
105
107
  captureError: true,
@@ -319,6 +321,7 @@ function tunnelProxy(server, proxy, type) {
319
321
  id: req.reqId,
320
322
  url: options.host,
321
323
  fc: req.fromComposer ? 1 : undefined,
324
+ testId: req._testId,
322
325
  startTime: Date.now(),
323
326
  rules: _rules,
324
327
  req: reqData,
package/lib/upgrade.js CHANGED
@@ -167,6 +167,7 @@ function upgradeHandler(req, socket) {
167
167
  socket.url = req.url;
168
168
  socket.fromTunnel = req.fromTunnel;
169
169
  socket.fromComposer = req.fromComposer;
170
+ socket._testId = req._testId;
170
171
  socket.enableXFF = req.enableXFF;
171
172
  socket._isInternalReq = req._isInternalReq;
172
173
  socket.isInternalUrl = req.isInternalUrl;
@@ -1127,6 +1127,10 @@ function isNum(n) {
1127
1127
  return typeof n === 'number';
1128
1128
  }
1129
1129
 
1130
+ exports.trimStr = function(str) {
1131
+ return typeof str === 'string' ? str.trim() : '';
1132
+ };
1133
+
1130
1134
  exports.parsePlainText = function (text, resolveKeys) {
1131
1135
  var result;
1132
1136
  text.split(LINE_END_RE).forEach(function(line) {
@@ -2034,4 +2038,10 @@ exports.writeTempFile = function (value, callback) {
2034
2038
  writeFile(path.join(TEMP_FILES_PATH, filename), value, function(err) {
2035
2039
  callback(err, filename);
2036
2040
  });
2037
- };
2041
+ };
2042
+
2043
+ var COMMENT_RE = /#[^\r\n]*/g;
2044
+
2045
+ exports.removeComment = function(text) {
2046
+ return text.replace(COMMENT_RE, '').trim();
2047
+ };
@@ -577,6 +577,16 @@ module.exports = function init(_proxy) {
577
577
  }
578
578
  return item;
579
579
  };
580
+ proxy.getTestItem = function(id) {
581
+ var list = Object.values(reqData);
582
+ for (var i = 0, len = list.length; i < len; i++) {
583
+ var item = list[i];
584
+ if (item.testId === id) {
585
+ return item;
586
+ }
587
+ }
588
+ };
589
+
580
590
  proxy.abortRequest = function (id) {
581
591
  var item = id && reqData[id];
582
592
  item && item.abort && item.abort();
package/lib/util/index.js CHANGED
@@ -29,6 +29,7 @@ var h2Consts = config.enableH2 ? require('http2').constants : {};
29
29
  var protocols = protoMgr.protocols;
30
30
  var resProtocols = protoMgr.resProtocols;
31
31
  var uid = config.uid;
32
+ var trimStr = common.trimStr;
32
33
  var parseQuery = common.parseQuery;
33
34
  var supportsBr = common.supportsBr;
34
35
  var isLocalIp = common.isLocalIp;
@@ -126,6 +127,7 @@ workerIndex = workerIndex >= 0 ? common.padLeft(config.workerIndex, 3) : '';
126
127
 
127
128
 
128
129
  exports.sendRes = common.sendRes;
130
+ exports.trimStr = trimStr;
129
131
  exports.THEME_STYLE = THEME_STYLE;
130
132
  exports.encodeNonLatin1Char = encodeNonLatin1Char;
131
133
  exports.isJson = common.isJson;
@@ -2359,15 +2361,6 @@ exports.transformReq = function(req, res, port, host, html) {
2359
2361
  return client;
2360
2362
  };
2361
2363
 
2362
- function trimStr(str) {
2363
- if (typeof str !== 'string') {
2364
- return '';
2365
- }
2366
- return str.trim();
2367
- }
2368
-
2369
- exports.trimStr = trimStr;
2370
-
2371
2364
  function hasHeaderRules(headers) {
2372
2365
  return (
2373
2366
  headers['x-whistle-rule-key'] ||
@@ -3389,19 +3382,25 @@ exports.parseRange = function (req, size) {
3389
3382
  };
3390
3383
 
3391
3384
  exports.parseClientInfo = function (req) {
3392
- if (req.headers[config.FROM_COM_HEADER] === '1') {
3385
+ var headers = req.headers;
3386
+ var testId = headers[config.TEST_HEADER];
3387
+ if (testId) {
3388
+ delete headers[config.TEST_HEADER];
3389
+ req._testId = testId;
3390
+ }
3391
+ if (headers[config.FROM_COM_HEADER] === '1') {
3393
3392
  req.fromComposer = true;
3394
- req._disabledProxyRules = !!req.headers[config.DISABLE_RULES_HEADER];
3395
- delete req.headers[config.FROM_COM_HEADER];
3396
- delete req.headers[config.DISABLE_RULES_HEADER];
3393
+ req._disabledProxyRules = !!headers[config.DISABLE_RULES_HEADER];
3394
+ delete headers[config.FROM_COM_HEADER];
3395
+ delete headers[config.DISABLE_RULES_HEADER];
3397
3396
  }
3398
3397
  var socket = req.socket || '';
3399
3398
  if (socket.fromTunnel) {
3400
3399
  req.fromTunnel = true;
3401
3400
  }
3402
- var clientInfo = req.headers[clientInfoKey];
3401
+ var clientInfo = headers[clientInfoKey];
3403
3402
  if (clientInfo) {
3404
- delete req.headers[clientInfoKey];
3403
+ delete headers[clientInfoKey];
3405
3404
  clientInfo = String(clientInfo).split(',');
3406
3405
  if (!net.isIP(clientInfo[0]) || !(clientInfo[1] > 0)) {
3407
3406
  return '';
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.3",
4
+ "version": "2.10.5",
5
5
  "dataDirname": ".whistle",
6
6
  "localUIHost": "local.whistlejs.com",
7
7
  "port": 8899,
@@ -53,7 +53,7 @@
53
53
  "parseurl": "^1.3.1",
54
54
  "pfork": "^0.6.3",
55
55
  "pipestream": "^0.7.5",
56
- "set-global-proxy": "^0.4.0",
56
+ "set-global-proxy": "^0.4.2",
57
57
  "sni": "1.0.0",
58
58
  "sockx": "^0.2.3",
59
59
  "starting": "^8.0.3",