whistle 2.10.5 → 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.
- package/assets/menu.html +2 -2
- package/assets/modal.html +2 -2
- package/assets/tab.html +2 -2
- package/bin/api.js +545 -0
- package/bin/import.js +3 -3
- package/bin/plugin.js +4 -8
- package/bin/use.js +22 -8
- package/bin/util.js +12 -7
- package/bin/whistle.js +11 -1
- package/biz/webui/cgi-bin/abort.js +14 -5
- package/biz/webui/cgi-bin/frames.js +12 -0
- package/biz/webui/cgi-bin/get-session.js +7 -3
- package/biz/webui/cgi-bin/is-enabled-https.js +6 -0
- package/biz/webui/cgi-bin/plugins/disable-plugin.js +16 -7
- package/biz/webui/cgi-bin/plugins/list.js +17 -0
- package/biz/webui/cgi-bin/plugins/plugin.js +21 -0
- package/biz/webui/cgi-bin/plugins/status.js +21 -0
- package/biz/webui/cgi-bin/rules/is-multi-select.js +7 -0
- package/biz/webui/cgi-bin/rules/move-top.js +11 -0
- package/biz/webui/cgi-bin/rules/select.js +1 -1
- package/biz/webui/cgi-bin/rules/select2.js +16 -0
- package/biz/webui/cgi-bin/rules/status.js +6 -0
- package/biz/webui/cgi-bin/rules/unselect.js +3 -2
- package/biz/webui/cgi-bin/rules/unselect2.js +18 -0
- package/biz/webui/cgi-bin/rules/value.js +19 -0
- package/biz/webui/cgi-bin/sessions.js +6 -0
- package/biz/webui/cgi-bin/util.js +45 -20
- package/biz/webui/htdocs/index.html +1 -1
- package/biz/webui/htdocs/js/index.js +51 -51
- package/biz/webui/lib/index.js +1 -3
- package/lib/https/index.js +7 -5
- package/lib/init.js +1 -0
- package/lib/inspectors/data.js +39 -34
- package/lib/inspectors/req.js +9 -6
- package/lib/inspectors/res.js +10 -14
- package/lib/plugins/index.js +2 -2
- package/lib/plugins/load-plugin.js +19 -8
- package/lib/rules/util.js +20 -10
- package/lib/service/composer.js +7 -22
- package/lib/service/data-center.js +0 -12
- package/lib/service/generate-saz.js +2 -2
- package/lib/service/service.js +8 -7
- package/lib/service/util.js +6 -16
- package/lib/socket-mgr.js +23 -17
- package/lib/tunnel.js +7 -17
- package/lib/upgrade.js +4 -8
- package/lib/util/common.js +78 -11
- package/lib/util/data-server.js +273 -0
- package/lib/util/file-mgr.js +7 -4
- package/lib/util/http-mgr.js +17 -9
- package/lib/util/index.js +67 -54
- package/lib/util/parse-url.js +11 -9
- package/lib/util/patch.js +2 -6
- package/lib/util/speed-transform.js +17 -6
- package/lib/util/whistle-transform.js +18 -7
- package/package.json +10 -7
package/lib/util/data-server.js
CHANGED
|
@@ -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/file-mgr.js
CHANGED
|
@@ -36,21 +36,24 @@ function readSingleFile(path, callback) {
|
|
|
36
36
|
return callback();
|
|
37
37
|
}
|
|
38
38
|
var stream = fs.createReadStream(convertSlash(path));
|
|
39
|
-
var done
|
|
39
|
+
var done;
|
|
40
|
+
var buf = [];
|
|
41
|
+
var len = 0;
|
|
40
42
|
var execCallback = function (err) {
|
|
41
43
|
if (done) {
|
|
42
44
|
return;
|
|
43
45
|
}
|
|
44
46
|
done = true;
|
|
45
47
|
stream.close();
|
|
46
|
-
callback(
|
|
48
|
+
callback(buf.length > 1 ? Buffer.concat(buf) : buf[0]);
|
|
47
49
|
};
|
|
48
50
|
stream.on('data', function (data) {
|
|
49
51
|
if (done) {
|
|
50
52
|
return;
|
|
51
53
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
len += data.length;
|
|
55
|
+
buf.push(data);
|
|
56
|
+
if (len > MAX_SIZE) {
|
|
54
57
|
execCallback();
|
|
55
58
|
}
|
|
56
59
|
});
|
package/lib/util/http-mgr.js
CHANGED
|
@@ -182,6 +182,10 @@ function gunzip(err, res, body, callback) {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
function getBody(body, len) {
|
|
186
|
+
return body.length > 1 ? Buffer.concat(body, len) : (body[0] || '');
|
|
187
|
+
}
|
|
188
|
+
|
|
185
189
|
function request(options, callback) {
|
|
186
190
|
loadPlugin(options, function (err) {
|
|
187
191
|
if (err) {
|
|
@@ -230,21 +234,23 @@ function request(options, callback) {
|
|
|
230
234
|
return callback(null, res, res);
|
|
231
235
|
}
|
|
232
236
|
addTimeout();
|
|
233
|
-
var body =
|
|
237
|
+
var body = [];
|
|
238
|
+
var len = 0;
|
|
234
239
|
res.on('data', function (data) {
|
|
235
|
-
|
|
240
|
+
len += data.length;
|
|
241
|
+
body.push(data);
|
|
236
242
|
addTimeout();
|
|
237
|
-
if (maxLength &&
|
|
243
|
+
if (maxLength && len > maxLength) {
|
|
238
244
|
var err;
|
|
239
245
|
if (!options.ignoreExceedError) {
|
|
240
246
|
err = new Error('The response body exceeded length limit');
|
|
241
247
|
err.code = EXCEED;
|
|
242
248
|
}
|
|
243
|
-
handleCallback(err, res, body);
|
|
249
|
+
handleCallback(err, res, getBody(body, len));
|
|
244
250
|
}
|
|
245
251
|
});
|
|
246
252
|
res.on('end', function() {
|
|
247
|
-
handleCallback(null, res, body);
|
|
253
|
+
handleCallback(null, res, getBody(body, len));
|
|
248
254
|
});
|
|
249
255
|
};
|
|
250
256
|
client = sendReq(options, function (err, res) {
|
|
@@ -293,7 +299,8 @@ function readFile(url, callback) {
|
|
|
293
299
|
|
|
294
300
|
var stream = fs.createReadStream(filePath, OPTIONS);
|
|
295
301
|
var done;
|
|
296
|
-
var body =
|
|
302
|
+
var body = [];
|
|
303
|
+
var len = 0;
|
|
297
304
|
var listener = function (err) {
|
|
298
305
|
if (done) {
|
|
299
306
|
return;
|
|
@@ -305,14 +312,15 @@ function readFile(url, callback) {
|
|
|
305
312
|
done = true;
|
|
306
313
|
data.mtime = time;
|
|
307
314
|
stream.close();
|
|
308
|
-
triggerChange(data, body);
|
|
315
|
+
triggerChange(data, body.join(''));
|
|
309
316
|
};
|
|
310
317
|
stream.on('data', function (text) {
|
|
311
318
|
if (done) {
|
|
312
319
|
return;
|
|
313
320
|
}
|
|
314
|
-
|
|
315
|
-
|
|
321
|
+
len += text.length;
|
|
322
|
+
body.push(text);
|
|
323
|
+
if (len > MAX_FILE_LEN) {
|
|
316
324
|
listener();
|
|
317
325
|
}
|
|
318
326
|
});
|
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;
|
|
@@ -747,24 +750,38 @@ exports.disableCSP = disableCSP;
|
|
|
747
750
|
|
|
748
751
|
var interfaces = os.networkInterfaces();
|
|
749
752
|
var hostname = os.hostname();
|
|
753
|
+
var serverInfo;
|
|
750
754
|
var simpleHostname = '';
|
|
751
755
|
var cpus = os.cpus();
|
|
752
756
|
var addressList = [];
|
|
753
757
|
var usiTimer;
|
|
754
758
|
|
|
755
|
-
function
|
|
759
|
+
function updateServerInfo() {
|
|
760
|
+
serverInfo = {ipv4: [], ipv6: []};
|
|
761
|
+
Object.keys(interfaces).forEach(function(ifname) {
|
|
762
|
+
interfaces[ifname].forEach(function (iface) {
|
|
763
|
+
if (iface.internal) {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
serverInfo[iface.family == 'IPv4' || iface.family === 4 ? 'ipv4' : 'ipv6'].push(iface.address);
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function updateSysInfo() {
|
|
756
772
|
clearTimeout(usiTimer);
|
|
757
773
|
interfaces = os.networkInterfaces();
|
|
774
|
+
updateServerInfo();
|
|
758
775
|
hostname = os.hostname();
|
|
759
776
|
addressList = [];
|
|
760
777
|
common.walkInterfaces(function (info) {
|
|
761
778
|
addressList.push(info.address.toLowerCase());
|
|
762
779
|
});
|
|
763
|
-
usiTimer = setTimeout(
|
|
780
|
+
usiTimer = setTimeout(updateSysInfo, 36000);
|
|
764
781
|
}
|
|
765
782
|
|
|
766
|
-
|
|
767
|
-
process.on('w2NetworkInterfacesChange',
|
|
783
|
+
updateSysInfo();
|
|
784
|
+
process.on('w2NetworkInterfacesChange', updateSysInfo);
|
|
768
785
|
|
|
769
786
|
if (isString(hostname)) {
|
|
770
787
|
simpleHostname = hostname.replace(/[^\w.-]+/g, '').substring(0, 20);
|
|
@@ -874,15 +891,13 @@ exports.removeClientId = function (headers) {
|
|
|
874
891
|
delete headers[config.CLIENT_ID_HEADER];
|
|
875
892
|
};
|
|
876
893
|
|
|
877
|
-
function networkInterfaces() {
|
|
878
|
-
return interfaces;
|
|
879
|
-
}
|
|
880
|
-
|
|
881
894
|
function getHostname() {
|
|
882
895
|
return hostname;
|
|
883
896
|
}
|
|
884
897
|
|
|
885
|
-
exports.
|
|
898
|
+
exports.getServerInfo = function() {
|
|
899
|
+
return serverInfo;
|
|
900
|
+
};
|
|
886
901
|
exports.hostname = getHostname;
|
|
887
902
|
|
|
888
903
|
function getProxyTunnelPath(req, isHttps) {
|
|
@@ -1613,35 +1628,35 @@ function getPipeIconvStream(headers) {
|
|
|
1613
1628
|
pipeStream.addTail(iconv.encodeStream(charset));
|
|
1614
1629
|
} else {
|
|
1615
1630
|
pipeStream.addHead(function (res, next) {
|
|
1616
|
-
var
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
function resolveCharset(chunk) {
|
|
1625
|
-
if (!charset) {
|
|
1626
|
-
if (chunk && buffer.length < 25600) {
|
|
1631
|
+
var buf = [];
|
|
1632
|
+
var len = 0;
|
|
1633
|
+
var resolveCharset = function(chunk) {
|
|
1634
|
+
if (chunk) {
|
|
1635
|
+
len += chunk.length;
|
|
1636
|
+
buf.push(chunk);
|
|
1637
|
+
if (len < 25600) {
|
|
1627
1638
|
return;
|
|
1628
1639
|
}
|
|
1629
|
-
charset = !buffer || isUtf8(buffer) ? 'utf8' : 'GB18030';
|
|
1630
1640
|
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1641
|
+
res.removeListener('data', resolveCharset);
|
|
1642
|
+
res.removeListener('end', resolveCharset);
|
|
1643
|
+
buf = buf.length > 1 ? Buffer.concat(buf, len) : buf[0];
|
|
1644
|
+
if (!buf) {
|
|
1645
|
+
return next(res);
|
|
1634
1646
|
}
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
}
|
|
1647
|
+
charset = isUtf8(buf) ? 'utf8' : 'GB18030';
|
|
1648
|
+
var decoder = iconv.decodeStream(charset);
|
|
1649
|
+
decoder.write(buf);
|
|
1650
|
+
next(res.pipe(decoder));
|
|
1651
|
+
buf = null;
|
|
1652
|
+
};
|
|
1653
|
+
|
|
1654
|
+
res.on('data', resolveCharset);
|
|
1655
|
+
res.on('end', resolveCharset);
|
|
1641
1656
|
});
|
|
1642
1657
|
|
|
1643
1658
|
pipeStream.addTail(function (src, next) {
|
|
1644
|
-
next(src.pipe(iconv.encodeStream(charset)));
|
|
1659
|
+
next(charset ? src.pipe(iconv.encodeStream(charset)) : src);
|
|
1645
1660
|
});
|
|
1646
1661
|
}
|
|
1647
1662
|
|
|
@@ -2452,10 +2467,7 @@ exports.getPluginModal = function(conf) {
|
|
|
2452
2467
|
};
|
|
2453
2468
|
|
|
2454
2469
|
function getString(str) {
|
|
2455
|
-
|
|
2456
|
-
return;
|
|
2457
|
-
}
|
|
2458
|
-
return str.trim();
|
|
2470
|
+
return isString(str) ? str.trim() : '';
|
|
2459
2471
|
}
|
|
2460
2472
|
|
|
2461
2473
|
exports.getString = getString;
|
|
@@ -2625,19 +2637,6 @@ exports.getStaticDir = function (conf) {
|
|
|
2625
2637
|
return toRelPath(staticDir.replace(/^\/+/, ''));
|
|
2626
2638
|
};
|
|
2627
2639
|
|
|
2628
|
-
function toString(str) {
|
|
2629
|
-
if (str != null) {
|
|
2630
|
-
if (typeof str === 'string') {
|
|
2631
|
-
return str;
|
|
2632
|
-
}
|
|
2633
|
-
try {
|
|
2634
|
-
return JSON.stringify(str) || '';
|
|
2635
|
-
} catch (e) {}
|
|
2636
|
-
}
|
|
2637
|
-
return '';
|
|
2638
|
-
}
|
|
2639
|
-
exports.toString = toString;
|
|
2640
|
-
|
|
2641
2640
|
var index = 0;
|
|
2642
2641
|
|
|
2643
2642
|
exports.getReqId = function (now) {
|
|
@@ -2965,7 +2964,7 @@ exports.setResCors = function (data, cors, req) {
|
|
|
2965
2964
|
} else if (autoComp) {
|
|
2966
2965
|
var method = req.headers['access-control-request-method'];
|
|
2967
2966
|
if (method) {
|
|
2968
|
-
setHeader(data, 'access-control-allow-
|
|
2967
|
+
setHeader(data, 'access-control-allow-methods', method);
|
|
2969
2968
|
}
|
|
2970
2969
|
}
|
|
2971
2970
|
|
|
@@ -3357,7 +3356,7 @@ exports.parseRange = function (req, size) {
|
|
|
3357
3356
|
}
|
|
3358
3357
|
var start = size;
|
|
3359
3358
|
var end = -1;
|
|
3360
|
-
range
|
|
3359
|
+
range.split(',').forEach(function (item) {
|
|
3361
3360
|
item = item.split('-');
|
|
3362
3361
|
var s = parseInt(item[0], 10);
|
|
3363
3362
|
var e = parseInt(item[1], 10);
|
|
@@ -3612,7 +3611,7 @@ function readOneChunk(stream, callback, timeout) {
|
|
|
3612
3611
|
}
|
|
3613
3612
|
var timer;
|
|
3614
3613
|
var handler = function (chunk) {
|
|
3615
|
-
|
|
3614
|
+
clearTimeout(timer);
|
|
3616
3615
|
stream.pause();
|
|
3617
3616
|
stream.removeListener('data', handler);
|
|
3618
3617
|
stream.removeListener('end', handler);
|
|
@@ -3686,10 +3685,9 @@ exports.getAuthBasic = function (auth) {
|
|
|
3686
3685
|
|
|
3687
3686
|
exports.delay = function (time, callback) {
|
|
3688
3687
|
if (time > 0) {
|
|
3689
|
-
setTimeout(callback, time);
|
|
3690
|
-
} else {
|
|
3691
|
-
callback();
|
|
3688
|
+
return setTimeout(callback, time);
|
|
3692
3689
|
}
|
|
3690
|
+
callback();
|
|
3693
3691
|
};
|
|
3694
3692
|
|
|
3695
3693
|
var F_HOST_RE = /\bhost\b/i;
|
|
@@ -4016,3 +4014,18 @@ exports.getFrameId = function() {
|
|
|
4016
4014
|
}
|
|
4017
4015
|
return Date.now() + '-00' + frameIndex;
|
|
4018
4016
|
};
|
|
4017
|
+
|
|
4018
|
+
exports.joinBuffer = function(buf, chunk) {
|
|
4019
|
+
if (!buf) {
|
|
4020
|
+
return chunk;
|
|
4021
|
+
}
|
|
4022
|
+
if (!Array.isArray(buf)) {
|
|
4023
|
+
return [buf, chunk];
|
|
4024
|
+
}
|
|
4025
|
+
buf.push(chunk);
|
|
4026
|
+
return buf;
|
|
4027
|
+
};
|
|
4028
|
+
|
|
4029
|
+
exports.formatBuffer = function(buf, len) {
|
|
4030
|
+
return Array.isArray(buf) ? Buffer.concat(buf, len) : buf;
|
|
4031
|
+
};
|
package/lib/util/parse-url.js
CHANGED
|
@@ -5,19 +5,21 @@ var URL_RE = /^([a-z0-9.+-]+:)\/\/([^/?#]*)(\/[^?#]*)?(\?[^#]*)?(#[\s\S]*)?$/i;
|
|
|
5
5
|
var HOST_RE = /^(.+)(?::(\d*))$/;
|
|
6
6
|
|
|
7
7
|
module.exports = function (url) {
|
|
8
|
-
|
|
8
|
+
var urlMatch = URL_RE.exec(url);
|
|
9
|
+
if (!urlMatch) {
|
|
9
10
|
return parseUrl(url);
|
|
10
11
|
}
|
|
11
|
-
var protocol =
|
|
12
|
-
var host =
|
|
13
|
-
var pathname =
|
|
14
|
-
var search =
|
|
15
|
-
var hash =
|
|
12
|
+
var protocol = urlMatch[1];
|
|
13
|
+
var host = urlMatch[2];
|
|
14
|
+
var pathname = urlMatch[3] || '/';
|
|
15
|
+
var search = urlMatch[4] || '';
|
|
16
|
+
var hash = urlMatch[5] || null;
|
|
16
17
|
var port = null;
|
|
17
18
|
var hostname = host;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
var hostMatch = HOST_RE.exec(host);
|
|
20
|
+
if (hostMatch) {
|
|
21
|
+
hostname = hostMatch[1];
|
|
22
|
+
port = hostMatch[2];
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
return {
|
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
|
-
|
|
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
|
|
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
|
-
|
|
18
|
+
inherits(SpeedTransform, Transform);
|
|
17
19
|
|
|
18
|
-
SpeedTransform.prototype
|
|
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
|
-
|
|
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
|
|
44
|
+
return self._setTimeout(cb, delay);
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
cb();
|