whistle 2.10.6 → 2.10.7

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 (52) hide show
  1. package/assets/menu.html +2 -2
  2. package/assets/modal.html +2 -2
  3. package/assets/tab.html +2 -2
  4. package/bin/api.js +545 -0
  5. package/bin/import.js +3 -3
  6. package/bin/plugin.js +4 -8
  7. package/bin/use.js +22 -8
  8. package/bin/util.js +10 -5
  9. package/bin/whistle.js +11 -1
  10. package/biz/webui/cgi-bin/abort.js +14 -5
  11. package/biz/webui/cgi-bin/frames.js +12 -0
  12. package/biz/webui/cgi-bin/is-enabled-https.js +6 -0
  13. package/biz/webui/cgi-bin/plugins/disable-plugin.js +16 -7
  14. package/biz/webui/cgi-bin/plugins/list.js +17 -0
  15. package/biz/webui/cgi-bin/plugins/plugin.js +21 -0
  16. package/biz/webui/cgi-bin/plugins/status.js +21 -0
  17. package/biz/webui/cgi-bin/rules/is-multi-select.js +7 -0
  18. package/biz/webui/cgi-bin/rules/move-top.js +11 -0
  19. package/biz/webui/cgi-bin/rules/select.js +1 -1
  20. package/biz/webui/cgi-bin/rules/select2.js +16 -0
  21. package/biz/webui/cgi-bin/rules/status.js +6 -0
  22. package/biz/webui/cgi-bin/rules/unselect.js +3 -2
  23. package/biz/webui/cgi-bin/rules/unselect2.js +18 -0
  24. package/biz/webui/cgi-bin/rules/value.js +19 -0
  25. package/biz/webui/cgi-bin/sessions.js +6 -0
  26. package/biz/webui/cgi-bin/util.js +14 -0
  27. package/biz/webui/htdocs/index.html +1 -1
  28. package/biz/webui/htdocs/js/index.js +32 -32
  29. package/biz/webui/lib/index.js +1 -3
  30. package/lib/https/index.js +4 -4
  31. package/lib/init.js +1 -0
  32. package/lib/inspectors/data.js +6 -4
  33. package/lib/inspectors/req.js +1 -2
  34. package/lib/inspectors/res.js +6 -10
  35. package/lib/plugins/index.js +2 -2
  36. package/lib/plugins/load-plugin.js +17 -6
  37. package/lib/rules/util.js +20 -10
  38. package/lib/service/composer.js +7 -22
  39. package/lib/service/data-center.js +0 -12
  40. package/lib/service/generate-saz.js +2 -2
  41. package/lib/service/service.js +6 -5
  42. package/lib/service/util.js +6 -16
  43. package/lib/socket-mgr.js +23 -17
  44. package/lib/tunnel.js +7 -17
  45. package/lib/upgrade.js +4 -8
  46. package/lib/util/common.js +75 -9
  47. package/lib/util/data-server.js +273 -0
  48. package/lib/util/index.js +8 -22
  49. package/lib/util/patch.js +2 -6
  50. package/lib/util/speed-transform.js +17 -6
  51. package/lib/util/whistle-transform.js +18 -7
  52. package/package.json +10 -7
package/lib/tunnel.js CHANGED
@@ -23,7 +23,6 @@ var X_RE = /^x/;
23
23
  var STATUS_CODES = require('http').STATUS_CODES || {};
24
24
  var getRawHeaderNames = hparser.getRawHeaderNames;
25
25
  var formatHeaders = hparser.formatHeaders;
26
- var getRawHeaders = hparser.getRawHeaders;
27
26
  var CRONET_RE = /\bCronet\b/;
28
27
  var SYS_HOST_RE = /^(?:[^/:]+\.)?(?:apple|cdn-apple|icloud|office|office365|mzstatic|apple-cloudkit|microsoft|parallels)\.(?:com|cn|com\.cn)$/;
29
28
  var TUNNEL_RE = /^tunnel:\/\//;
@@ -104,6 +103,7 @@ function tunnelProxy(server, proxy, type) {
104
103
  testId: req._testId,
105
104
  startTime: now,
106
105
  dnsTime: now,
106
+ servername: reqSocket.useSNI === hostname ? undefined : reqSocket.useSNI,
107
107
  captureError: true,
108
108
  rules: req.rules,
109
109
  req: {
@@ -382,7 +382,7 @@ function tunnelProxy(server, proxy, type) {
382
382
  var reqDelay = util.getMatcherValue(_rules.reqDelay);
383
383
  data.requestTime = data.dnsTime = Date.now();
384
384
  if (reqDelay > 0) {
385
- setTimeout(handleConnect, reqDelay);
385
+ util.setDelayTimer(reqSocket, setTimeout(handleConnect, reqDelay));
386
386
  } else {
387
387
  handleConnect();
388
388
  }
@@ -404,8 +404,7 @@ function tunnelProxy(server, proxy, type) {
404
404
  var proxyUrl;
405
405
  if (tunnelPort) {
406
406
  proxyUrl = 'proxy://127.0.0.1:' + tunnelPort;
407
- reqSocket.customParser = req.customParser =
408
- util.getParserStatus(req);
407
+ reqSocket.customParser = req.customParser = util.getParserStatus(req);
409
408
  pluginMgr.addSessionInfo(req, _rules);
410
409
  headers[config.PLUGIN_HOOK_NAME_HEADER] =
411
410
  config.PLUGIN_HOOKS.TUNNEL;
@@ -553,7 +552,7 @@ function tunnelProxy(server, proxy, type) {
553
552
  }
554
553
  };
555
554
  if (reqDelay > 0) {
556
- setTimeout(connectProxy, reqDelay);
555
+ util.setDelayTimer(reqSocket, setTimeout(connectProxy, reqDelay));
557
556
  } else {
558
557
  connectProxy();
559
558
  }
@@ -687,7 +686,7 @@ function tunnelProxy(server, proxy, type) {
687
686
  };
688
687
  var resDelay = util.getMatcherValue(_rules.resDelay);
689
688
  if (resDelay > 0) {
690
- setTimeout(handleEstablished, resDelay);
689
+ util.setDelayTimer(reqSocket, setTimeout(handleEstablished, resDelay));
691
690
  } else {
692
691
  handleEstablished();
693
692
  }
@@ -770,23 +769,14 @@ function tunnelProxy(server, proxy, type) {
770
769
  util.deleteResHeaders(req, resHeaders);
771
770
  code = util.getMatcherValue(reqRules.replaceStatus) || code;
772
771
  var isSuccess = code == 200;
773
- var message =
774
- isSuccess
775
- ? 'Connection Established'
776
- : STATUS_CODES[code] || 'unknown';
777
- var statusLine = ['HTTP/1.1', code, message].join(' ');
772
+ var message = isSuccess ? 'Connection Established' : STATUS_CODES[code] || 'unknown';
778
773
  var curHeaders = resHeaders;
779
- var rawData = statusLine;
780
774
  if (req.fromComposer) {
781
775
  curHeaders = extend({}, resHeaders);
782
776
  curHeaders['x-whistle-req-id'] = req.reqId;
783
777
  util.setFramesMode(curHeaders, isSuccess && inspect);
784
778
  }
785
- curHeaders = getRawHeaders(formatHeaders(curHeaders, rawHeaderNames));
786
- if (curHeaders) {
787
- rawData += '\r\n' + curHeaders;
788
- }
789
- rawData += '\r\n\r\n';
779
+ var rawData = common.getHttpMeta('HTTP/1.1', code, message, curHeaders, rawHeaderNames);
790
780
  try {
791
781
  if (code != 200) {
792
782
  reqSocket.end(rawData, cb);
package/lib/upgrade.js CHANGED
@@ -6,9 +6,7 @@ var util = require('./util');
6
6
  var config = require('./config');
7
7
  var common = require('./util/common');
8
8
 
9
- var formatHeaders = hparser.formatHeaders;
10
9
  var getRawHeaderNames = hparser.getRawHeaderNames;
11
- var getRawHeaders = hparser.getRawHeaders;
12
10
  var PLUGIN_PATH_RE =
13
11
  /^\/(\.\.\.whistle-path\.5b6af7b9884e1165\.\.\.\/\/\/)?(whistle|plugin)\.([^/?#]+)\/?/;
14
12
 
@@ -105,12 +103,10 @@ function upgradeHandler(req, socket) {
105
103
  util.addTunnelData(socket, headers);
106
104
  socket._clientId = util.getComposerClientId(headers);
107
105
  var getBuffer = function (method, newHeaders, path) {
108
- var rawData = (method || 'GET') + ' ' + (path || socket.url || req.url) + ' HTTP/1.1';
109
- newHeaders = formatHeaders(newHeaders || headers, req.rawHeaders);
110
- if (newHeaders = getRawHeaders(newHeaders)) {
111
- rawData += '\r\n' + newHeaders;
112
- }
113
- return Buffer.from(rawData + '\r\n\r\n');
106
+ method = method || 'GET';
107
+ path = path || socket.url || req.url;
108
+ newHeaders = newHeaders || headers;
109
+ return Buffer.from(common.getHttpMeta(method, path, 'HTTP/1.1', newHeaders, req.rawHeaders));
114
110
  };
115
111
  var sniPlugin = headers[util.SNI_PLUGIN_HEADER];
116
112
  if (sniPlugin) {
@@ -12,6 +12,7 @@ var json5 = require('json5');
12
12
  var extend = require('extend');
13
13
  var tls = require('tls');
14
14
  var https = require('https');
15
+ var hparser = require('hparser');
15
16
  var zlibx = require('./zlib');
16
17
  var pkgConf = require('../../package.json');
17
18
  var isUtf8 = require('./is-utf8');
@@ -72,10 +73,22 @@ var TOKEN = '\r\u0000\n\u0003\r';
72
73
  var PROP_SEP_RE = /(\\*)([|&]|\\[stnrfv])/g;
73
74
  var TEMP_FILE_RE = /^(?:\/?temp\/)?([\da-f]{64})(?:\.[\w.-]+)?$/;
74
75
  var TEMP_FILES_PATH = path.join(getWhistlePath(), 'temp_files');
76
+ var SAVED_SESSIONS_FILE_RE = /_([1-9]\d*)_(\d+)$/;
77
+
78
+ exports.getSavedFileItem = function(filename) {
79
+ var result = SAVED_SESSIONS_FILE_RE.exec(filename);
80
+ return result && {
81
+ count: +result[1],
82
+ time: +result[2],
83
+ filename: filename.slice(0, - result[1].length - result[2].length - 2)
84
+ };
85
+ };
75
86
 
76
87
 
77
88
  exports.WHISTLE_UID_HEADER = '_x-whistle-uid_';
78
89
 
90
+ exports.isUtf8 = isUtf8;
91
+
79
92
  exports.getTempFile = function(str) {
80
93
  var match = TEMP_FILE_RE.exec(str);
81
94
  return match && match[1];
@@ -670,18 +683,29 @@ exports.isGroup = function(name) {
670
683
  };
671
684
 
672
685
  var UTF8_RE = /^utf-?8$/i;
686
+
687
+ function getUtf8Buf(buffer, charset) {
688
+ charset = isString(charset) ? charset.trim() : '';
689
+ if (UTF8_RE.test(charset) || isUtf8(buffer)) {
690
+ return buffer;
691
+ }
692
+ try {
693
+ buffer = iconv.encode(buffer, charset || 'GB18030');
694
+ } catch (e) {}
695
+ return buffer;
696
+ }
697
+
698
+ exports.getUtf8Buf = getUtf8Buf;
699
+
700
+
673
701
  function bufferToString(buffer, encoding) {
674
- if (typeof encoding === 'string') {
675
- encoding = encoding.trim();
676
- } else {
677
- encoding = null;
702
+ if (!buffer) {
703
+ return '';
678
704
  }
679
- if (Buffer.isBuffer(buffer) && !UTF8_RE.test(encoding) && !isUtf8(buffer)) {
680
- try {
681
- buffer = iconv.encode(buffer, encoding || 'GB18030');
682
- } catch (e) {}
705
+ if (Buffer.isBuffer(buffer)) {
706
+ buffer = getUtf8Buf(buffer, encoding);
683
707
  }
684
- return String(buffer || '');
708
+ return String(buffer);
685
709
  }
686
710
 
687
711
  exports.bufferToString = bufferToString;
@@ -852,9 +876,24 @@ exports.getHostPort = function(str) {
852
876
  }
853
877
  };
854
878
 
879
+ function clearDelayTimer(socket) {
880
+ var timer = socket._w2Timer_;
881
+ if (timer) {
882
+ clearTimeout(timer);
883
+ socket._w2Timer_ = null;
884
+ }
885
+ }
886
+
887
+ exports.clearDelayTimer = clearDelayTimer;
888
+ exports.setDelayTimer = function (socket, id) {
889
+ socket._w2Timer_ = id;
890
+ };
891
+
892
+
855
893
  exports.onSocketEnd = function(socket, callback) {
856
894
  var execCallback = function (err) {
857
895
  socket._hasError = true;
896
+ clearDelayTimer(socket);
858
897
  if (callback) {
859
898
  callback(err);
860
899
  callback = null;
@@ -970,6 +1009,10 @@ function isString(str) {
970
1009
 
971
1010
  exports.isString = isString;
972
1011
 
1012
+ exports.isStr = function(str) {
1013
+ return typeof str === 'string';
1014
+ };
1015
+
973
1016
  function isSafeNumStr(str) {
974
1017
  if (str == '0') {
975
1018
  return true;
@@ -1429,6 +1472,18 @@ exports.sendGzip = function (req, res, data) {
1429
1472
  });
1430
1473
  };
1431
1474
 
1475
+ exports.toString = function (str) {
1476
+ if (str != null) {
1477
+ if (typeof str === 'string') {
1478
+ return str;
1479
+ }
1480
+ try {
1481
+ return JSON.stringify(str) || '';
1482
+ } catch (e) {}
1483
+ }
1484
+ return '';
1485
+ };
1486
+
1432
1487
  exports.sendGzipText = function(req, res, headers, text, gzipText) {
1433
1488
  if (!text || !canGzip(req) || (!gzipText && text.length < GZIP_THRESHOLD)) {
1434
1489
  headers && res.writeHead(200, headers);
@@ -2046,3 +2101,14 @@ var COMMENT_RE = /#[^\r\n]*/g;
2046
2101
  exports.removeComment = function(text) {
2047
2102
  return text.replace(COMMENT_RE, '').trim();
2048
2103
  };
2104
+
2105
+ function getHttpMeta(f1, f2, f3, headers, rawHeaders) {
2106
+ var statusLine = [f1, f2, f3].join(' ');
2107
+ if (headers) {
2108
+ headers = hparser.formatHeaders(headers, rawHeaders);
2109
+ headers = hparser.getRawHeaders(headers);
2110
+ }
2111
+ return (headers ? statusLine + '\r\n' + headers : statusLine) + '\r\n\r\n';
2112
+ }
2113
+
2114
+ exports.getHttpMeta = getHttpMeta;
@@ -31,6 +31,58 @@ var framesCache = [];
31
31
  var framesMap = {};
32
32
  var proxy, binded;
33
33
  var clearCount = 0;
34
+ var MAX_COUNT = 120;
35
+
36
+ function formatApiOptions(options) {
37
+ var result = { count: MAX_COUNT };
38
+ if (options) {
39
+ var count = options.count;
40
+ if (count > 0 && count < MAX_COUNT) {
41
+ result.count = count;
42
+ }
43
+ var startTime = options.startTime > 0 ? options.startTime + '' : null;
44
+ var startId = util.isString(options.startId) ? options.startId : null;
45
+ if (startId && startTime && startId < startTime) {
46
+ startId = startTime;
47
+ }
48
+ result.startId = startId || startTime;
49
+ result.latest = options.latest;
50
+ }
51
+ return result;
52
+ }
53
+
54
+ function getItemId(item, name) {
55
+ return name ? item[name] : item;
56
+ }
57
+
58
+ function getIdIndex(arr, target, name) {
59
+ if (!target) {
60
+ return 0;
61
+ }
62
+ var left = 0;
63
+ var right = arr.length - 1;
64
+
65
+ while (left <= right) {
66
+ if (getItemId(arr[left], name) > target) {
67
+ return left;
68
+ }
69
+ var count = right - left;
70
+ if (!count) {
71
+ return -1;
72
+ }
73
+ if (count === 1) {
74
+ return getItemId(arr[right], name) > target ? right : -1;
75
+ }
76
+ var mid = left + Math.floor(count / 2);
77
+ var val = getItemId(arr[mid], name);
78
+ if (val > target) {
79
+ right = mid;
80
+ } else {
81
+ left = mid + 1;
82
+ }
83
+ }
84
+ return -1;
85
+ }
34
86
 
35
87
  function enable() {
36
88
  if (binded) {
@@ -595,6 +647,227 @@ module.exports = function init(_proxy) {
595
647
  return getFrames(options.curReqId, options.lastFrameId);
596
648
  };
597
649
 
650
+ proxy.getFramesForApi = function (options) {
651
+ var reqId = options.reqId;
652
+ var isServer = options.from === 'server';
653
+ var isClient = options.from === 'client';
654
+ var isAll = !isServer && !isClient;
655
+ var params = formatApiOptions(options);
656
+ var index = getIdIndex(framesCache, params.startId, 'frameId');
657
+ var result = [];
658
+ if (index === -1) {
659
+ return result;
660
+ }
661
+ var count = params.count;
662
+ var len = framesCache.length;
663
+ var addFrame = function(i) {
664
+ var frame = framesCache[i];
665
+ if ((isAll || isClient === !!frame.isClient) && frame.reqId === reqId) {
666
+ result.push(decodeData(frame));
667
+ if (--count <= 0) {
668
+ return true;
669
+ }
670
+ }
671
+ };
672
+ if (params.latest) {
673
+ for (var i = len - 1; i >= index; i--) {
674
+ if (addFrame(i)) {
675
+ break;
676
+ }
677
+ }
678
+ result.reverse();
679
+ } else {
680
+ for (; index < len; index++) {
681
+ if (addFrame(index)) {
682
+ break;
683
+ }
684
+ }
685
+ }
686
+ return result;
687
+ };
688
+
689
+ function checkUrl(session, subUrl) {
690
+ if (!subUrl) {
691
+ return true;
692
+ }
693
+ var url = session.isHttps ? 'tunnel://' + session.url : session.url.toLowerCase();
694
+ return url.indexOf(subUrl) !== -1;
695
+ }
696
+
697
+ var WS_RE = /^wss?:\/\//;
698
+ var FONT_RE = /font\//i;
699
+ var MEDIA_RE = /audio\/|video\/|application\/vnd.apple.mpegurl|application\/dash+xml/i;
700
+ var WASM_RE = /application\/wasm/i;
701
+ var TYPES = ['IMG', 'HTML', 'CSS', 'JS', 'JSON'];
702
+ var ALIAS_TYPES = {
703
+ WEBSOCKET: 'WS',
704
+ WSS: 'WS',
705
+ IMAGE: 'IMG',
706
+ JAVASCRIPT: 'JS',
707
+ STYLE: 'CSS',
708
+ MATCHED: 'RULES',
709
+ EDIT: 'COMPOSER'
710
+ };
711
+ var RAW_TYPES = ['FONT', 'MEDIA', 'WASM'];
712
+
713
+ function hasError(item) {
714
+ var statusCode = item.res && item.res.statusCode;
715
+ return item.reqError || item.resError || (item.customData && item.customData.error) ||
716
+ (statusCode && (!/^\d+$/.test(statusCode) || statusCode >= 400));
717
+ }
718
+
719
+ function checkType(session, filterType) {
720
+ if (!filterType) {
721
+ return true;
722
+ }
723
+ filterType = ALIAS_TYPES[filterType] || filterType;
724
+ if (filterType === 'CAPTUREERROR') {
725
+ return session.captureError;
726
+ }
727
+ if (filterType === 'WS') {
728
+ return WS_RE.test(session.url);
729
+ }
730
+ if (filterType === 'TUNNEL') {
731
+ return session.isHttps;
732
+ }
733
+ if (filterType === 'RULES') {
734
+ return session.rules && Object.keys(session.rules).length > 0;
735
+ }
736
+ var rawType = util.getRawType(session.res);
737
+ var type;
738
+ for (var i = 0, len = RAW_TYPES.length; i < len; i++) {
739
+ type = RAW_TYPES[i];
740
+ if (filterType === type) {
741
+ var p = type === 'FONT' ? FONT_RE : type === 'MEDIA' ? MEDIA_RE : WASM_RE;
742
+ return p.test(rawType);
743
+ }
744
+ }
745
+
746
+ var curType = util.getContentType(rawType);
747
+ for (i = 0, len = TYPES.length; i < len; i++) {
748
+ type = TYPES[i];
749
+ if (filterType === type) {
750
+ return curType === (i ? type : 'IMG');
751
+ }
752
+ }
753
+ if (filterType === 'ERROR') {
754
+ return hasError(session);
755
+ }
756
+ if (filterType === 'MOCK') {
757
+ var rules = session.rules || '';
758
+ return rules.rule && rules.rule.isLoc;
759
+ }
760
+ if (filterType === 'IMPORT') {
761
+ return session.importedData;
762
+ }
763
+ if (filterType === 'COMPOSER') {
764
+ return session.fc;
765
+ }
766
+ }
767
+
768
+ function formatHeaderFilter(filter) {
769
+ if (!filter) {
770
+ return;
771
+ }
772
+ var name = filter.name || filter.key;
773
+ if (typeof name !== 'string') {
774
+ return;
775
+ }
776
+ return {
777
+ name: name.trim(),
778
+ subValue: filter.subValue == null ? null : String(filter.subValue).toLowerCase()
779
+ };
780
+ }
781
+
782
+ function filterHeader(headers, filter) {
783
+ if (!filter) {
784
+ return true;
785
+ }
786
+ if (!headers) {
787
+ return false;
788
+ }
789
+ var value = headers[filter.name];
790
+ if (value == null) {
791
+ return false;
792
+ }
793
+ var subValue = filter.subValue;
794
+ var noSubValue = !subValue;
795
+ if (noSubValue || !value) {
796
+ return noSubValue;
797
+ }
798
+ value = Array.isArray(value) ? value.join('\n') : String(value);
799
+ return value.toLowerCase().indexOf(subValue) !== -1;
800
+ }
801
+
802
+ function checkMethod(session, method) {
803
+ if (!method) {
804
+ return true;
805
+ }
806
+ return method.indexOf(session.req.method) !== -1;
807
+ }
808
+
809
+ function checkStatus(session, status) {
810
+ return !status || status.indexOf(String(session.res.statusCode).toUpperCase()) !== -1;
811
+ }
812
+
813
+
814
+ proxy.getSessionsForApi = function (options) {
815
+ var params = formatApiOptions(options);
816
+ var index = getIdIndex(ids, params.startId);
817
+ var result = [];
818
+ if (index === -1) {
819
+ return result;
820
+ }
821
+ var count = params.count;
822
+ var len = ids.length;
823
+ var subUrl = util.getString(options.subUrl).toLowerCase();
824
+ var type = util.getString(options.type).toUpperCase();
825
+ var method = options.method;
826
+ var status = options.statusCode || options.status;
827
+ var reqHeader = formatHeaderFilter(options.reqHeader);
828
+ var resHeader = formatHeaderFilter(options.resHeader);
829
+ if (Array.isArray(method)) {
830
+ method = method.join();
831
+ } else {
832
+ method = util.getString(method);
833
+ }
834
+ if (Array.isArray(status)) {
835
+ status = status.join();
836
+ } else if (status) {
837
+ status = String(status);
838
+ }
839
+ method = method && method.toUpperCase().split(',');
840
+ status = status && status.toUpperCase().split(',');
841
+ var addSession = function(i) {
842
+ var session = reqData[ids[i]];
843
+ if (checkUrl(session, subUrl) && checkMethod(session, method)
844
+ && checkStatus(session, status) && filterHeader(session.req.headers, reqHeader)
845
+ && filterHeader(session.res.headers, resHeader) && checkType(session, type)) {
846
+ toBase64String(session.req);
847
+ toBase64String(session.res);
848
+ result.push(session);
849
+ if (--count <= 0) {
850
+ return true;
851
+ }
852
+ }
853
+ };
854
+ if (params.latest) {
855
+ for (var i = len - 1; i >= index; i--) {
856
+ if (addSession(i)) {
857
+ break;
858
+ }
859
+ }
860
+ result.reverse();
861
+ } else {
862
+ for (; index < len; index++) {
863
+ if (addSession(index)) {
864
+ break;
865
+ }
866
+ }
867
+ }
868
+ return result;
869
+ };
870
+
598
871
  function getNetworkTime(id) {
599
872
  var item = id && reqData[id];
600
873
  return item && {
package/lib/util/index.js CHANGED
@@ -126,7 +126,10 @@ exports.ADDITIONAL_HEAD = 'x-whistle-additional-headers';
126
126
  workerIndex = workerIndex >= 0 ? common.padLeft(config.workerIndex, 3) : '';
127
127
 
128
128
 
129
+ exports.toString = common.toString;
129
130
  exports.sendRes = common.sendRes;
131
+ exports.setDelayTimer = common.setDelayTimer;
132
+ exports.clearDelayTimer = common.clearDelayTimer;
130
133
  exports.trimStr = trimStr;
131
134
  exports.THEME_STYLE = THEME_STYLE;
132
135
  exports.encodeNonLatin1Char = encodeNonLatin1Char;
@@ -2464,10 +2467,7 @@ exports.getPluginModal = function(conf) {
2464
2467
  };
2465
2468
 
2466
2469
  function getString(str) {
2467
- if (!isString(str)) {
2468
- return;
2469
- }
2470
- return str.trim();
2470
+ return isString(str) ? str.trim() : '';
2471
2471
  }
2472
2472
 
2473
2473
  exports.getString = getString;
@@ -2637,19 +2637,6 @@ exports.getStaticDir = function (conf) {
2637
2637
  return toRelPath(staticDir.replace(/^\/+/, ''));
2638
2638
  };
2639
2639
 
2640
- function toString(str) {
2641
- if (str != null) {
2642
- if (typeof str === 'string') {
2643
- return str;
2644
- }
2645
- try {
2646
- return JSON.stringify(str) || '';
2647
- } catch (e) {}
2648
- }
2649
- return '';
2650
- }
2651
- exports.toString = toString;
2652
-
2653
2640
  var index = 0;
2654
2641
 
2655
2642
  exports.getReqId = function (now) {
@@ -2977,7 +2964,7 @@ exports.setResCors = function (data, cors, req) {
2977
2964
  } else if (autoComp) {
2978
2965
  var method = req.headers['access-control-request-method'];
2979
2966
  if (method) {
2980
- setHeader(data, 'access-control-allow-method', method);
2967
+ setHeader(data, 'access-control-allow-methods', method);
2981
2968
  }
2982
2969
  }
2983
2970
 
@@ -3624,7 +3611,7 @@ function readOneChunk(stream, callback, timeout) {
3624
3611
  }
3625
3612
  var timer;
3626
3613
  var handler = function (chunk) {
3627
- timer && clearTimeout(timer);
3614
+ clearTimeout(timer);
3628
3615
  stream.pause();
3629
3616
  stream.removeListener('data', handler);
3630
3617
  stream.removeListener('end', handler);
@@ -3698,10 +3685,9 @@ exports.getAuthBasic = function (auth) {
3698
3685
 
3699
3686
  exports.delay = function (time, callback) {
3700
3687
  if (time > 0) {
3701
- setTimeout(callback, time);
3702
- } else {
3703
- callback();
3688
+ return setTimeout(callback, time);
3704
3689
  }
3690
+ callback();
3705
3691
  };
3706
3692
 
3707
3693
  var F_HOST_RE = /\bhost\b/i;
package/lib/util/patch.js CHANGED
@@ -4,6 +4,7 @@ var Socket = require('net').Socket;
4
4
  var http = require('http');
5
5
  var https = require('https');
6
6
  var hparser = require('hparser');
7
+ var common = require('./common');
7
8
 
8
9
  var httpRequest = http.request;
9
10
  var httpsRequest = https.request;
@@ -133,12 +134,7 @@ http.request = function (options) {
133
134
  headers['x-whistle-policy'] = 'intercept';
134
135
  delete headers['X-Whistle-Policy'];
135
136
  }
136
- headers = hparser.getRawHeaders(headers);
137
- var rawData = 'CONNECT ' + tunnelPath + ' HTTP/1.1';
138
- if (headers) {
139
- rawData += '\r\n' + headers;
140
- }
141
- rawData += '\r\n\r\n';
137
+ var rawData = common.getHttpMeta('CONNECT', tunnelPath, 'HTTP/1.1', headers);
142
138
  if (res.statusCode === 200 && res.headers['x-whistle-allow-tunnel-ack']) {
143
139
  rawData = '1' + rawData;
144
140
  }
@@ -1,9 +1,11 @@
1
1
  var Transform = require('pipestream').Transform;
2
- var util = require('util');
2
+ var inherits = require('util').inherits;
3
+ var setDelayTimer = require('./common').setDelayTimer;
3
4
 
4
- function SpeedTransform(options) {
5
+ function SpeedTransform(options, req) {
5
6
  Transform.call(this);
6
7
  options = options || {};
8
+ this._req = req;
7
9
  var value = parseInt((options.speed * 1000) / 8);
8
10
  if (value > 0) {
9
11
  this._speed = value;
@@ -13,13 +15,22 @@ function SpeedTransform(options) {
13
15
  }
14
16
  }
15
17
 
16
- util.inherits(SpeedTransform, Transform);
18
+ inherits(SpeedTransform, Transform);
17
19
 
18
- SpeedTransform.prototype._transform = function (chunk, encoding, callback) {
20
+ var proto = SpeedTransform.prototype;
21
+
22
+ proto._setTimeout = function (callback, delay) {
23
+ var timer = setTimeout(callback, delay);
24
+ if (this._req) {
25
+ setDelayTimer(this._req, timer);
26
+ }
27
+ };
28
+
29
+ proto._transform = function (chunk, encoding, callback) {
19
30
  var self = this;
20
31
  var cb = function () {
21
32
  if (chunk && self._speed) {
22
- setTimeout(function () {
33
+ self._setTimeout(function () {
23
34
  callback(null, chunk);
24
35
  }, Math.round((chunk.length * 1000) / self._speed));
25
36
  } else {
@@ -30,7 +41,7 @@ SpeedTransform.prototype._transform = function (chunk, encoding, callback) {
30
41
  if (self._delay) {
31
42
  var delay = self._delay;
32
43
  self._delay = null;
33
- return setTimeout(cb, delay);
44
+ return self._setTimeout(cb, delay);
34
45
  }
35
46
 
36
47
  cb();