whistle 2.9.37 → 2.9.38-beta

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.
@@ -5,13 +5,12 @@ var express = require('express');
5
5
  var os = require('os');
6
6
  var format = require('util').format;
7
7
  var LRU = require('lru-cache');
8
- var iconv = require('iconv-lite');
9
8
  var wsParser = require('ws-parser');
10
9
  var http = require('http');
11
10
  var https = require('https');
12
11
  var extend = require('extend');
12
+ var gzip = require('zlib').gzip;
13
13
  var request = require('../util/http-mgr').request;
14
- var isUtf8 = require('../util/is-utf8');
15
14
  var Storage = require('../rules/storage');
16
15
  var getServer = require('hagent').create(null, 40500);
17
16
  var Buffer = require('safe-buffer').Buffer;
@@ -341,7 +340,7 @@ var addParserApi = function (req, conn, state, reqId) {
341
340
  }
342
341
  if (Array.isArray(data.toClient)) {
343
342
  data.toClient.forEach(function (frame) {
344
- var buf = toBuffer(frame.base64);
343
+ var buf = base64ToBuffer(frame.base64);
345
344
  try {
346
345
  buf && req.emit('sendToClient', buf, frame.binary);
347
346
  } catch (e) {}
@@ -349,7 +348,7 @@ var addParserApi = function (req, conn, state, reqId) {
349
348
  }
350
349
  if (Array.isArray(data.toServer)) {
351
350
  data.toServer.forEach(function (frame) {
352
- var buf = toBuffer(frame.base64);
351
+ var buf = base64ToBuffer(frame.base64);
353
352
  try {
354
353
  buf && req.emit('sendToServer', buf, frame.binary);
355
354
  } catch (e) {}
@@ -411,7 +410,7 @@ var ADDITIONAL_FIELDS = [
411
410
 
412
411
  function getPluginVars(req, key) {
413
412
  var value = req.headers[key];
414
- value = toBuffer(value);
413
+ value = base64ToBuffer(value);
415
414
  if (value) {
416
415
  delete req.headers[key];
417
416
  try {
@@ -465,7 +464,7 @@ var initReq = function (req, res, isServer) {
465
464
  oReq.isUIRequest = req.isUIRequest =
466
465
  req.headers[pluginOpts.UI_REQUEST_HEADER] == '1';
467
466
  oReq.enableCapture = req.headers[pluginOpts.ENABLE_CAPTURE_HEADER] == '1';
468
- oReq.isFromPlugin = req.headers[pluginOpts.PLUGIN_REQUEST_HEADER] == '1';
467
+ oReq.isFromPlugin = req.fromPlugin = oReq.fromPlugin = req.headers[pluginOpts.PLUGIN_REQUEST_HEADER] == '1';
469
468
  oReq.ruleValue = getValue(req, pluginOpts.RULE_VALUE_HEADER);
470
469
  oReq.ruleUrl = getValue(req, pluginOpts.RULE_URL_HEADER) || oReq.ruleValue;
471
470
  oReq.pipeValue = getValue(req, pluginOpts.PIPE_VALUE_HEADER);
@@ -542,7 +541,7 @@ var getOptions = function (opts, binary, toServer) {
542
541
  }
543
542
  return binary ? BINARY_OPTIONS : '';
544
543
  };
545
- var toBuffer = function (base64) {
544
+ var base64ToBuffer = function (base64) {
546
545
  if (base64) {
547
546
  try {
548
547
  return new Buffer(base64, 'base64');
@@ -550,16 +549,11 @@ var toBuffer = function (base64) {
550
549
  }
551
550
  };
552
551
  var getBuffer = function (item) {
553
- return toBuffer(item.base64);
552
+ return base64ToBuffer(item.base64);
554
553
  };
555
554
  var getText = function (item) {
556
- var body = toBuffer(item.base64) || '';
557
- if (body && !isUtf8(body)) {
558
- try {
559
- body = iconv.encode(body, 'GB18030');
560
- } catch (e) {}
561
- }
562
- return body + '';
555
+ var body = base64ToBuffer(item.base64) || '';
556
+ return common.bufferToString(body);
563
557
  };
564
558
 
565
559
  var defineProps = function (obj) {
@@ -885,31 +879,33 @@ function handleError(socket, sender, receiver) {
885
879
  receiver && (receiver.onerror = emitError);
886
880
  }
887
881
 
888
- function toBinary(data) {
889
- if (!data || Buffer.isBuffer(data)) {
890
- return data && data.length && data;
891
- }
892
- if (typeof data !== 'string') {
893
- try {
894
- data = JSON.stringify(data);
895
- } catch (e) {
896
- return;
897
- }
898
- }
899
- return Buffer.from(data);
900
- }
882
+ var GZIP_RE = /^\s*gzip\s*$/i;
901
883
 
902
- function getCustomBody(body, headers) {
884
+ function getCustomBody(body, req, cb) {
885
+ var headers = req.headers;
903
886
  if (body && typeof body.pipe === 'function') {
904
887
  delete headers['content-length'];
905
- return body;
888
+ return cb(body);
906
889
  }
907
- body = toBinary(body) || '';
908
- if (headers['content-length'] != null) {
909
- headers['content-length'] = body ? body.length : 0;
910
- delete headers['transfer-encoding'];
890
+ var handleCb = function() {
891
+ if (headers['content-length'] != null) {
892
+ headers['content-length'] = body.length;
893
+ delete headers['transfer-encoding'];
894
+ }
895
+ cb(body);
896
+ };
897
+ if (Buffer.isBuffer(body)) {
898
+ return handleCb();
899
+ }
900
+ var encoding = headers['content-encoding'];
901
+ body = common.toBuffer(body, req) || '';
902
+ if (!GZIP_RE.test(encoding)) {
903
+ return handleCb();
911
904
  }
912
- return body;
905
+ gzip(body, function(_, result) {
906
+ body = result || body;
907
+ handleCb();
908
+ });
913
909
  }
914
910
 
915
911
  function wrapTunnelWriter(socket, toServer) {
@@ -918,7 +914,7 @@ function wrapTunnelWriter(socket, toServer) {
918
914
  var sender = wsParser.getSender(socket, toServer);
919
915
  handleError(socket, sender);
920
916
  socket.write = function (chunk, encoding, cb) {
921
- if ((chunk = toBinary(chunk))) {
917
+ if ((chunk = common.toBuffer(chunk))) {
922
918
  if (encoding === 'binary') {
923
919
  return write.call(this, chunk, encoding, cb);
924
920
  }
@@ -931,7 +927,7 @@ function wrapTunnelWriter(socket, toServer) {
931
927
  };
932
928
  if (toServer) {
933
929
  socket.write = function (chunk, opts, cb) {
934
- if ((chunk = toBinary(chunk))) {
930
+ if ((chunk = common.toBuffer(chunk))) {
935
931
  if (opts === 'binary') {
936
932
  return write.call(this, chunk, opts, cb);
937
933
  }
@@ -939,12 +935,12 @@ function wrapTunnelWriter(socket, toServer) {
939
935
  }
940
936
  };
941
937
  socket.writeText = function (chunk) {
942
- if ((chunk = toBinary(chunk))) {
938
+ if ((chunk = common.toBuffer(chunk))) {
943
939
  sender.send(chunk, getOptions(null, false, true));
944
940
  }
945
941
  };
946
942
  socket.writeBin = function (chunk) {
947
- if ((chunk = toBinary(chunk))) {
943
+ if ((chunk = common.toBuffer(chunk))) {
948
944
  sender.send(chunk, getOptions(null, true, true));
949
945
  }
950
946
  };
@@ -956,7 +952,7 @@ function wrapTunnelWriter(socket, toServer) {
956
952
  };
957
953
  } else {
958
954
  socket.write = function (chunk, encoding, cb) {
959
- if ((chunk = toBinary(chunk))) {
955
+ if ((chunk = common.toBuffer(chunk))) {
960
956
  if (encoding === 'binary') {
961
957
  return write.call(this, chunk, encoding, cb);
962
958
  }
@@ -995,9 +991,9 @@ function setReqRules(uri, reqRules) {
995
991
  if (!reqRules) {
996
992
  return;
997
993
  }
998
- var rules = reqRules.rules || reqRules;
994
+ var rules = formatRules(reqRules);
999
995
  var values = reqRules.values;
1000
- if (typeof rules !== 'string') {
996
+ if (!rules) {
1001
997
  return;
1002
998
  }
1003
999
  uri.headers = uri.headers || {};
@@ -1039,7 +1035,7 @@ function addFrameHandler(req, socket, maxWsPayload, fromClient, toServer) {
1039
1035
  emit.call(socket, 'data', chunk, opts);
1040
1036
  };
1041
1037
  socket.write = function (chunk, opts, cb) {
1042
- if ((chunk = toBinary(chunk))) {
1038
+ if ((chunk = common.toBuffer(chunk))) {
1043
1039
  if (opts === 'binary') {
1044
1040
  return write.call(this, chunk, opts, cb);
1045
1041
  }
@@ -1047,12 +1043,12 @@ function addFrameHandler(req, socket, maxWsPayload, fromClient, toServer) {
1047
1043
  }
1048
1044
  };
1049
1045
  socket.writeText = function (chunk) {
1050
- if ((chunk = toBinary(chunk))) {
1046
+ if ((chunk = common.toBuffer(chunk))) {
1051
1047
  sender.send(chunk, getOptions(null, false, toServer));
1052
1048
  }
1053
1049
  };
1054
1050
  socket.writeBin = function (chunk) {
1055
- if ((chunk = toBinary(chunk))) {
1051
+ if ((chunk = common.toBuffer(chunk))) {
1056
1052
  sender.send(chunk, getOptions(null, true, toServer));
1057
1053
  }
1058
1054
  };
@@ -1114,6 +1110,14 @@ function destroySocket() {
1114
1110
  this.destroy();
1115
1111
  }
1116
1112
 
1113
+ function formatRules(rules) {
1114
+ if (Array.isArray(rules)) {
1115
+ return rules.join('\n');
1116
+ }
1117
+ rules = rules.rules || rules;
1118
+ return typeof rules === 'string' ? rules : undefined;
1119
+ }
1120
+
1117
1121
  module.exports = async function (options, callback) {
1118
1122
  var root = options.value;
1119
1123
  if (options.isDev) {
@@ -1180,13 +1184,17 @@ module.exports = async function (options, callback) {
1180
1184
  var boundPort = config.port;
1181
1185
  var PLUGIN_HOOKS = config.PLUGIN_HOOKS;
1182
1186
  var RES_RULES_HEAD = config.RES_RULES_HEAD;
1183
- var getRulesStr = function (obj) {
1187
+ var setResRules = function(headers, obj) {
1188
+ var rules = formatRules(obj);
1189
+ if (!rules) {
1190
+ return;
1191
+ }
1184
1192
  obj = JSON.stringify({
1185
- rules: typeof obj === 'string' ? obj : obj.rules,
1193
+ rules: rules,
1186
1194
  values: obj.values,
1187
1195
  root: root
1188
1196
  });
1189
- return encodeURIComponent(obj);
1197
+ headers[RES_RULES_HEAD] = encodeURIComponent(obj);
1190
1198
  };
1191
1199
  var SHOW_LOGIN_BOX = options.SHOW_LOGIN_BOX;
1192
1200
  FROM_TUNNEL_HEADER = options.FROM_TUNNEL_HEADER;
@@ -2036,7 +2044,7 @@ module.exports = async function (options, callback) {
2036
2044
  headers = headers || svrRes.headers;
2037
2045
  if (resRules) {
2038
2046
  headers = headers || {};
2039
- headers[RES_RULES_HEAD] = getRulesStr(resRules);
2047
+ setResRules(headers, resRules);
2040
2048
  req.setResRules = res.setResRules = noop;
2041
2049
  resRules = null;
2042
2050
  }
@@ -2108,15 +2116,17 @@ module.exports = async function (options, callback) {
2108
2116
  }
2109
2117
  };
2110
2118
  if (handleRes) {
2111
- common.readStream(_res, function(buffer) {
2112
- handleRes(buffer, function(result) {
2119
+ common.readStream(_res, function(rawBuffer) {
2120
+ handleRes(rawBuffer, function(result) {
2113
2121
  if (result) {
2114
2122
  req.setResRules(result.rules);
2115
- _response(getCustomBody(result.body, _res.headers), result.trailers);
2123
+ getCustomBody(result.body, _res, function(body) {
2124
+ _response(body, result.trailers);
2125
+ });
2116
2126
  } else {
2117
2127
  _response('');
2118
2128
  }
2119
- });
2129
+ }, _res);
2120
2130
  });
2121
2131
  } else {
2122
2132
  _response();
@@ -2131,15 +2141,17 @@ module.exports = async function (options, callback) {
2131
2141
  }
2132
2142
  };
2133
2143
  if (handleReq) {
2134
- common.readStream(req, function(buffer) {
2135
- handleReq(buffer, function(result) {
2144
+ common.readStream(req, function(rawBuffer) {
2145
+ handleReq(rawBuffer, function(result) {
2136
2146
  if (result) {
2137
2147
  req.setReqRules(result.rules);
2138
- _request(getCustomBody(result.body, req.headers) || '', result.uri || result.url);
2148
+ getCustomBody(result.body, req, function(body) {
2149
+ _request(body, result.uri || result.url);
2150
+ });
2139
2151
  } else {
2140
2152
  _request('');
2141
2153
  }
2142
- });
2154
+ }, req);
2143
2155
  });
2144
2156
  } else {
2145
2157
  _request();
@@ -2220,7 +2232,7 @@ module.exports = async function (options, callback) {
2220
2232
  headers = headers || svrRes.headers;
2221
2233
  if (resRules) {
2222
2234
  headers = headers || {};
2223
- headers[RES_RULES_HEAD] = getRulesStr(resRules);
2235
+ setResRules(headers, resRules);
2224
2236
  req.setResRules = socket.setResRules = noop;
2225
2237
  resRules = null;
2226
2238
  }
@@ -2,7 +2,12 @@ var os = require('os');
2
2
  var path = require('path');
3
3
  var fse = require('fs-extra2');
4
4
  var fs = require('fs');
5
+ var iconv = require('iconv-lite');
6
+ var qs = require('qs');
7
+ var Buffer = require('safe-buffer').Buffer;
8
+ var zlib = require('./zlib');
5
9
  var pkgConf = require('../../package.json');
10
+ var isUtf8 = require('./is-utf8');
6
11
 
7
12
  var HEAD_RE = /^head$/i;
8
13
  var HOME_DIR_RE = /^[~~]\//;
@@ -325,12 +330,113 @@ exports.isGroup = function(name) {
325
330
  return name && name[0] === '\r';
326
331
  };
327
332
 
333
+ var UTF8_RE = /^utf-?8$/i;
334
+ function bufferToString(buffer, encoding) {
335
+ if (typeof encoding === 'string') {
336
+ encoding = encoding.trim();
337
+ } else {
338
+ encoding = null;
339
+ }
340
+ if (Buffer.isBuffer(buffer) && !UTF8_RE.test(encoding) && !isUtf8(buffer)) {
341
+ try {
342
+ buffer = iconv.encode(buffer, encoding || 'GB18030');
343
+ } catch (e) {}
344
+ }
345
+ return String(buffer || '');
346
+ }
347
+
348
+ exports.bufferToString = bufferToString;
349
+
350
+ var URLENCODED_RE = /application\/x-www-form-urlencoded/i;
351
+ var POST_RE = /^post$/i;
352
+
353
+ function isUrlEncoded(req) {
354
+ var type = req.method && req.headers && req.headers['content-type'];
355
+ return type && POST_RE.test(req.method) && URLENCODED_RE.test(type);
356
+ }
357
+
358
+ exports.isUrlEncoded = isUrlEncoded;
359
+
360
+ function queryToString(query, req) {
361
+ if (!req || typeof query !== 'object' || !isUrlEncoded(req)) {
362
+ return query;
363
+ }
364
+ return qs.stringify(query);
365
+ }
366
+
367
+ exports.toBuffer = function(data, req) {
368
+ data = queryToString(data, req);
369
+ if (!data || Buffer.isBuffer(data)) {
370
+ return data && data.length && data;
371
+ }
372
+ if (typeof data !== 'string') {
373
+ try {
374
+ data = JSON.stringify(data);
375
+ } catch (e) {
376
+ return;
377
+ }
378
+ }
379
+ return Buffer.from(data);
380
+ };
381
+
382
+
328
383
  exports.readStream = function(stream, callback) {
329
384
  var buffer = null;
330
385
  stream.on('data', function(chunk) {
331
386
  buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
332
387
  });
333
388
  stream.once('end', function() {
389
+ var buf;
390
+ var text;
391
+ var json;
392
+ var err;
393
+ var jsonErr;
394
+ stream.zlib = zlib;
395
+ var encoding = stream.headers && stream.headers['content-encoding'];
396
+ var getBuffer = function(cb) {
397
+ if (err || buf !== undefined) {
398
+ return cb(err, buf);
399
+ }
400
+ zlib.unzip(encoding, buffer, function(e, ctn) {
401
+ err = e;
402
+ buf = ctn || null;
403
+ cb(err, buf);
404
+ });
405
+ };
406
+ var getText = function(cb, ecd) {
407
+ if (err || text != null) {
408
+ return cb(err, text);
409
+ }
410
+ getBuffer(function(e, ctn) {
411
+ err = e;
412
+ text = bufferToString(ctn, ecd);
413
+ cb(err, text);
414
+ });
415
+ };
416
+ stream.getBuffer = getBuffer;
417
+ stream.getText = getText;
418
+ stream.getJson = function(cb, ecd) {
419
+ getText(function() {
420
+ if (err || jsonErr || !text || json !== undefined) {
421
+ return cb(err || jsonErr, json);
422
+ }
423
+ try {
424
+ if (isUrlEncoded(stream)) {
425
+ json = qs.parse(text);
426
+ } else {
427
+ text = text.trim();
428
+ if (text[0] === '[' || text[0] === '{') {
429
+ json = JSON.parse(text) || null;
430
+ } else {
431
+ json = null;
432
+ }
433
+ }
434
+ } catch (e) {
435
+ jsonErr = e;
436
+ }
437
+ cb(jsonErr, json);
438
+ }, ecd);
439
+ };
334
440
  callback(buffer);
335
441
  });
336
442
  };
@@ -347,16 +347,16 @@ module.exports = function init(_proxy) {
347
347
  return result;
348
348
  }
349
349
  function checkClientIp(item, filter) {
350
- var clientId = getClientId(item);
351
- var ipList = filter.ipList;
352
350
  var clientIp = item.req.ip;
353
351
  if (filter.clientIp) {
354
- return clientId === filter.clientIp;
352
+ return clientIp === filter.clientIp;
355
353
  }
356
354
  if (filter.ip && clientIp === filter.ip) {
357
355
  return true;
358
356
  }
359
357
  // 有 clientId 过滤条件时,必须匹配 clientId
358
+ var clientId = getClientId(item);
359
+ var ipList = filter.ipList;
360
360
  if (filter.clientId) {
361
361
  if (clientId === filter.clientId) {
362
362
  return true;
package/lib/util/index.js CHANGED
@@ -1823,14 +1823,8 @@ exports.getClientPort = function (req) {
1823
1823
 
1824
1824
  exports.removeIPV6Prefix = removeIPV6Prefix;
1825
1825
 
1826
- function isUrlEncoded(req) {
1827
- return (
1828
- /^post$/i.test(req.method) &&
1829
- /urlencoded/i.test(req.headers && req.headers['content-type'])
1830
- );
1831
- }
1826
+ exports.isUrlEncoded = common.isUrlEncoded;
1832
1827
 
1833
- exports.isUrlEncoded = isUrlEncoded;
1834
1828
  function isJSONContent(req) {
1835
1829
  if (!hasRequestBody(req)) {
1836
1830
  return false;
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.9.37",
4
+ "version": "2.9.38-beta",
5
5
  "dataDirname": ".whistle",
6
6
  "localUIHost": "local.whistlejs.com",
7
7
  "port": 8899,