whistle 2.10.9 → 2.10.10
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/js/log.js +4 -0
- package/bin/api.js +85 -1
- package/bin/util.js +1 -1
- package/biz/webui/cgi-bin/util.js +1 -1
- package/biz/webui/cgi-bin/values/add.js +15 -13
- package/biz/webui/htdocs/index.html +1 -1
- package/biz/webui/htdocs/js/index.js +50 -51
- package/biz/webui/lib/index.js +39 -30
- package/lib/config.js +10 -13
- package/lib/handlers/file-proxy.js +1 -1
- package/lib/https/ca.js +17 -16
- package/lib/https/h2.js +53 -34
- package/lib/https/index.js +11 -10
- package/lib/inspectors/data.js +7 -3
- package/lib/inspectors/log.js +14 -33
- package/lib/inspectors/res.js +2 -2
- package/lib/inspectors/rules.js +11 -6
- package/lib/inspectors/weinre.js +1 -1
- package/lib/plugins/index.js +44 -53
- package/lib/plugins/load-plugin.js +31 -40
- package/lib/rules/dns.js +1 -1
- package/lib/rules/index.js +1 -0
- package/lib/rules/rules.js +1 -1
- package/lib/rules/util.js +2 -7
- package/lib/service/composer.js +7 -3
- package/lib/service/service.js +3 -5
- package/lib/socket-mgr.js +1 -1
- package/lib/util/common.js +56 -33
- package/lib/util/data-server.js +5 -1
- package/lib/util/drain.js +5 -6
- package/lib/util/file-mgr.js +1 -1
- package/lib/util/http-mgr.js +6 -2
- package/lib/util/index.js +29 -23
- package/lib/util/speed-transform.js +2 -2
- package/lib/util/transproto.js +4 -3
- package/lib/util/whistle-transform.js +2 -2
- package/lib/util/zlib.js +1 -1
- package/package.json +3 -3
package/biz/webui/lib/index.js
CHANGED
|
@@ -35,6 +35,7 @@ var uploadUrlencodedParser = bodyParser.urlencoded(UPLOAD_PARSE_CONF);
|
|
|
35
35
|
var uploadJsonParser = bodyParser.json(UPLOAD_PARSE_CONF);
|
|
36
36
|
var GET_METHOD_RE = /^get$/i;
|
|
37
37
|
var WEINRE_RE = /^\/weinre\/.*/;
|
|
38
|
+
var IMPORT_CGI = '/cgi-bin/import-remote';
|
|
38
39
|
var ALLOW_PLUGIN_PATHS = ['/cgi-bin/rules/list2', '/cgi-bin/values/list2', '/cgi-bin/get-custom-certs-info'];
|
|
39
40
|
var DONT_CHECK_PATHS = ['/cgi-bin/server-info', '/cgi-bin/plugins/is-enable', '/cgi-bin/plugins/get-plugins',
|
|
40
41
|
'/preview.html', '/cgi-bin/rootca', '/cgi-bin/check-update', '/cgi-bin/log/set', '/cgi-bin/status'];
|
|
@@ -70,11 +71,14 @@ function isTempFile(query) {
|
|
|
70
71
|
return common.getTempFile(query.filename);
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
function sendForbidden(res) {
|
|
75
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
76
|
+
res.end('{"ec":0,"value":"","forbidden":true}');
|
|
77
|
+
}
|
|
78
|
+
|
|
73
79
|
function sendToService(req, res) {
|
|
74
80
|
if (!hasLogin() && req.path === '/cgi-bin/temp/get' && !isTempFile(req.query)) {
|
|
75
|
-
res
|
|
76
|
-
res.end('{"ec":0,"value":"","forbidden":true}');
|
|
77
|
-
return;
|
|
81
|
+
return sendForbidden(res);
|
|
78
82
|
}
|
|
79
83
|
loadService(function(err, options) {
|
|
80
84
|
if (err) {
|
|
@@ -135,6 +139,9 @@ function requireLogin(req, res, msg) {
|
|
|
135
139
|
}
|
|
136
140
|
return res.status(404).end();
|
|
137
141
|
}
|
|
142
|
+
if (req.path === IMPORT_CGI) {
|
|
143
|
+
return sendForbidden(res);
|
|
144
|
+
}
|
|
138
145
|
res.setHeader('WWW-Authenticate', ' Basic realm=User Login');
|
|
139
146
|
res.setHeader('Content-Type', 'text/html; charset=utf8');
|
|
140
147
|
res.status(401).end(msg || 'Access denied, please <a href="javascript:;" onclick="location.reload()">try again</a>');
|
|
@@ -410,32 +417,7 @@ function cgiHandler(req, res) {
|
|
|
410
417
|
handleResponse();
|
|
411
418
|
});
|
|
412
419
|
}
|
|
413
|
-
|
|
414
|
-
var url = req.query.url;
|
|
415
|
-
if (HTTP_RE.test(url)) {
|
|
416
|
-
util.request({
|
|
417
|
-
url: url,
|
|
418
|
-
maxLength: MAX_LEN
|
|
419
|
-
}, function(err, body, r) {
|
|
420
|
-
if (err) {
|
|
421
|
-
var msg = err.code === 'EEXCEED' ? 'The size of response body exceeds 6MB' : err.message;
|
|
422
|
-
return res.json({ec: 2, em: msg});
|
|
423
|
-
}
|
|
424
|
-
var status = r.statusCode;
|
|
425
|
-
if (status !== 200) {
|
|
426
|
-
var em = status > 200 && status < 400 ? 'No data' : 'Request failed';
|
|
427
|
-
return res.json({ec: 2, em: em + ' (statusCode: ' + status + ')'});
|
|
428
|
-
}
|
|
429
|
-
return res.json({ec: 0, body: body});
|
|
430
|
-
});
|
|
431
|
-
} else if (util.isString(url)) {
|
|
432
|
-
req.url = '/cgi-bin/temp/get?filename=' + encodeURIComponent(url);
|
|
433
|
-
req.query.filename = url;
|
|
434
|
-
sendToService(req, res);
|
|
435
|
-
} else {
|
|
436
|
-
res.json({ec: 400, em: 'Bad url'});
|
|
437
|
-
}
|
|
438
|
-
});
|
|
420
|
+
|
|
439
421
|
app.all('/service/*', sendToService);
|
|
440
422
|
app.all('/cgi-bin/service/*', sendToService);
|
|
441
423
|
app.all('/cgi-bin/sessions/*', sendToService);
|
|
@@ -448,7 +430,7 @@ function readPluginPage(req, res, plugin, html, config) {
|
|
|
448
430
|
res.write(config);
|
|
449
431
|
res.write(html);
|
|
450
432
|
var index = req.path.indexOf('/', 1);
|
|
451
|
-
if (index === -1) {
|
|
433
|
+
if (index === -1 || common.existsUpPath(req.path)) {
|
|
452
434
|
res.end();
|
|
453
435
|
} else {
|
|
454
436
|
var filepath = req.path.substring(index + 1);
|
|
@@ -528,6 +510,33 @@ app.use(function(req, res, next) {
|
|
|
528
510
|
}
|
|
529
511
|
});
|
|
530
512
|
|
|
513
|
+
app.get(IMPORT_CGI, function(req, res) {
|
|
514
|
+
var url = req.query.url;
|
|
515
|
+
if (HTTP_RE.test(url)) {
|
|
516
|
+
util.request({
|
|
517
|
+
url: url,
|
|
518
|
+
maxLength: MAX_LEN
|
|
519
|
+
}, function(err, body, r) {
|
|
520
|
+
if (err) {
|
|
521
|
+
var msg = err.code === 'EEXCEED' ? 'The size of response body exceeds 6MB' : err.message;
|
|
522
|
+
return res.json({ec: 2, em: msg});
|
|
523
|
+
}
|
|
524
|
+
var status = r.statusCode;
|
|
525
|
+
if (status !== 200) {
|
|
526
|
+
var em = status > 200 && status < 400 ? 'No data' : 'Request failed';
|
|
527
|
+
return res.json({ec: 2, em: em + ' (statusCode: ' + status + ')'});
|
|
528
|
+
}
|
|
529
|
+
return res.json({ec: 0, body: body});
|
|
530
|
+
});
|
|
531
|
+
} else if (util.isString(url)) {
|
|
532
|
+
req.url = '/cgi-bin/temp/get?filename=' + encodeURIComponent(url);
|
|
533
|
+
req.query.filename = url;
|
|
534
|
+
sendToService(req, res);
|
|
535
|
+
} else {
|
|
536
|
+
res.json({ec: 400, em: 'Bad url'});
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
|
|
531
540
|
app.post('/cgi-bin/composer', function(req, res) {
|
|
532
541
|
req.headers[config.CLIENT_IP_HEADER] = util.getClientIp(req);
|
|
533
542
|
req.headers[config.CLIENT_PORT_HEADER] = util.getClientPort(req);
|
package/lib/config.js
CHANGED
|
@@ -26,7 +26,6 @@ var httpsAgents = new LRU({ max: 360 });
|
|
|
26
26
|
var socksAgents = new LRU({ max: 100 });
|
|
27
27
|
var version = process.version.substring(1).split('.');
|
|
28
28
|
var disableAgent = version[0] > 10;
|
|
29
|
-
var useVerbatim = version[0] >= 17;
|
|
30
29
|
var hasIPv6First = version[0] >= 22 && version[1] >= 1;
|
|
31
30
|
var hasDnsOrder = typeof dns.setDefaultResultOrder === 'function';
|
|
32
31
|
var uid = Date.now().toString(16) + (process.pid * 10000 + Math.floor(Math.random() * 10000)).toString(16);
|
|
@@ -61,15 +60,10 @@ var variableProperties = [
|
|
|
61
60
|
'disableInstaller'
|
|
62
61
|
];
|
|
63
62
|
if (hasDnsOrder) {
|
|
64
|
-
|
|
65
|
-
config.verbatim = 2;
|
|
66
|
-
} else {
|
|
67
|
-
config.verbatim = 1;
|
|
68
|
-
}
|
|
63
|
+
config.verbatim = hasIPv6First ? 2 : 1;
|
|
69
64
|
}
|
|
70
65
|
|
|
71
66
|
config.appName = common.upperFirst(config.name);
|
|
72
|
-
config.defaultDnsOrder = useVerbatim ? 1 : 2;
|
|
73
67
|
config.uid = uid;
|
|
74
68
|
config.rejectUnauthorized = false;
|
|
75
69
|
config.enableH2 = version[0] > 12; // 支持 HTTP2 要求的最低 Node 版本
|
|
@@ -119,7 +113,7 @@ config.PLUGIN_HOOKS = {
|
|
|
119
113
|
|
|
120
114
|
var KEEP_ALIVE_MSECS = 10000;
|
|
121
115
|
var CONN_TIMEOUT = common.CONN_TIMEOUT;
|
|
122
|
-
var DNS_ORDERS_OPTS = ['', '
|
|
116
|
+
var DNS_ORDERS_OPTS = ['', 'ipv4first', 'ipv4first', hasIPv6First ? 'ipv6first' : '', '', 'verbatim'];
|
|
123
117
|
var DOMAIN_STAR_RE = /([*~]+)(\\.)?/g;
|
|
124
118
|
|
|
125
119
|
config.CONN_TIMEOUT = CONN_TIMEOUT;
|
|
@@ -305,13 +299,16 @@ function getAuths(_url) {
|
|
|
305
299
|
|
|
306
300
|
exports.getAuths = getAuths;
|
|
307
301
|
|
|
302
|
+
function getDnsOrder(order) {
|
|
303
|
+
return order == 1 ? 2 : order;
|
|
304
|
+
}
|
|
305
|
+
|
|
308
306
|
function setDefaultResultOrder(order) {
|
|
309
307
|
if (!hasDnsOrder) {
|
|
310
308
|
return;
|
|
311
309
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return;
|
|
310
|
+
if (DNS_ORDERS_OPTS.indexOf(order) === -1) {
|
|
311
|
+
order = DNS_ORDERS_OPTS[order] || DNS_ORDERS_OPTS[1];
|
|
315
312
|
}
|
|
316
313
|
config.ipv4First = false;
|
|
317
314
|
config.ipv6First = false;
|
|
@@ -321,10 +318,10 @@ function setDefaultResultOrder(order) {
|
|
|
321
318
|
config.ipv4First = true;
|
|
322
319
|
}
|
|
323
320
|
try {
|
|
324
|
-
if (dns.getDefaultResultOrder() !== order) {
|
|
321
|
+
if (!dns.getDefaultResultOrder || dns.getDefaultResultOrder() !== order) {
|
|
325
322
|
dns.setDefaultResultOrder(order);
|
|
326
323
|
}
|
|
327
|
-
config.dnsOrder = DNS_ORDERS_OPTS.indexOf(order);
|
|
324
|
+
config.dnsOrder = getDnsOrder(DNS_ORDERS_OPTS.indexOf(order));
|
|
328
325
|
return true;
|
|
329
326
|
} catch (e) {}
|
|
330
327
|
}
|
package/lib/https/ca.js
CHANGED
|
@@ -43,9 +43,9 @@ var ILEGAL_CHAR_RE = /[^a-z\d-]/i;
|
|
|
43
43
|
// https://cloud.tencent.com/developer/ask/sof/58155
|
|
44
44
|
var RANDOM_SERIAL = '/' + Date.now() + '/' + process.pid + '/' + Math.floor(Math.random() * 100);
|
|
45
45
|
var ONE_DAY = 1000 * 60 * 60 * 24;
|
|
46
|
-
var
|
|
47
|
-
var
|
|
48
|
-
var
|
|
46
|
+
var ONE_YEAR = ONE_DAY * 365;
|
|
47
|
+
var MIN_DATE = ONE_DAY * 7;
|
|
48
|
+
var MAX_DATE = ONE_DAY * 36;
|
|
49
49
|
var PORT_RE = /:\d*$/;
|
|
50
50
|
var pureCertFiles = {};
|
|
51
51
|
var customRoot;
|
|
@@ -71,13 +71,16 @@ function resetAllCerts() {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// When delay is larger than 2147483647 or less than 1, the delay will be set to 1. Non-integer delays are truncated to an integer.
|
|
74
|
-
var
|
|
74
|
+
var startTime = Date.now();
|
|
75
|
+
var INTERVAL = ONE_DAY;
|
|
76
|
+
var MAX_AGE = MAX_DATE - INTERVAL * 2;
|
|
75
77
|
var timer = setInterval(function () {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
var now = Date.now();
|
|
79
|
+
if (now - startTime >= MAX_AGE) {
|
|
80
|
+
startTime = now;
|
|
78
81
|
resetAllCerts();
|
|
79
82
|
}
|
|
80
|
-
},
|
|
83
|
+
}, INTERVAL);
|
|
81
84
|
|
|
82
85
|
if (timer && typeof timer.unref === 'function') {
|
|
83
86
|
timer.unref();
|
|
@@ -553,18 +556,16 @@ function createCert(publicKey, serialNumber, isShortPeriod) {
|
|
|
553
556
|
var cert = pki.createCertificate();
|
|
554
557
|
cert.publicKey = publicKey;
|
|
555
558
|
cert.serialNumber = serialNumber || '01';
|
|
556
|
-
var
|
|
557
|
-
|
|
559
|
+
var now = Date.now();
|
|
560
|
+
// https://www.ssls.com/blog/apples-new-ssl-lifetime-limitation-and-what-it-means-for-you/
|
|
561
|
+
// https://chromium.googlesource.com/chromium/src/+/refs/heads/master/net/cert/cert_verify_proc.cc#900
|
|
558
562
|
if (isShortPeriod) {
|
|
559
|
-
cert.validity.notBefore = new Date(
|
|
563
|
+
cert.validity.notBefore = new Date(now - MIN_DATE);
|
|
564
|
+
cert.validity.notAfter = new Date(now + MAX_DATE);
|
|
560
565
|
} else {
|
|
561
|
-
cert.validity.notBefore = new Date();
|
|
562
|
-
cert.validity.
|
|
566
|
+
cert.validity.notBefore = new Date(now - ONE_YEAR);
|
|
567
|
+
cert.validity.notAfter = new Date(now + ONE_YEAR * 10);
|
|
563
568
|
}
|
|
564
|
-
// https://www.ssls.com/blog/apples-new-ssl-lifetime-limitation-and-what-it-means-for-you/
|
|
565
|
-
// https://chromium.googlesource.com/chromium/src/+/refs/heads/master/net/cert/cert_verify_proc.cc#900
|
|
566
|
-
cert.validity.notAfter = new Date();
|
|
567
|
-
cert.validity.notAfter.setFullYear(curYear + (isShortPeriod ? 1 : 10));
|
|
568
569
|
return cert;
|
|
569
570
|
}
|
|
570
571
|
|
package/lib/https/h2.js
CHANGED
|
@@ -33,33 +33,43 @@ function onClose(stream, callback) {
|
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function closeClose(client, name) {
|
|
37
|
+
try {
|
|
38
|
+
client.close();
|
|
39
|
+
} catch (e) {}
|
|
40
|
+
delete clients[name];
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
setInterval(function () {
|
|
37
44
|
var now = Date.now();
|
|
38
45
|
Object.keys(clients).forEach(function (name) {
|
|
39
46
|
var client = clients[name];
|
|
40
|
-
if (now - client._updateTime > CACHE_TIMEOUT) {
|
|
41
|
-
client
|
|
42
|
-
delete clients[name];
|
|
47
|
+
if (isClosed(client) || now - client._updateTime > CACHE_TIMEOUT) {
|
|
48
|
+
closeClose(client, name);
|
|
43
49
|
}
|
|
44
50
|
});
|
|
45
51
|
}, INTERVAL);
|
|
46
52
|
|
|
47
|
-
function
|
|
53
|
+
function getOrigin(req) {
|
|
54
|
+
return (req.useHttpH2 ? 'http' : 'https') + '://' + req.headers.host;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getKey(options, req) {
|
|
48
58
|
var proxyOpts = options._proxyOptions;
|
|
49
59
|
var proxyType = '';
|
|
50
60
|
if (proxyOpts) {
|
|
51
|
-
var
|
|
52
|
-
(proxyOpts.headers && proxyOpts.headers['proxy-authorization']) || '';
|
|
61
|
+
var headers = proxyOpts.headers || {};
|
|
53
62
|
proxyType = [
|
|
54
63
|
proxyOpts.proxyType,
|
|
55
64
|
proxyOpts.proxyHost,
|
|
56
65
|
proxyOpts.proxyPort,
|
|
57
|
-
|
|
66
|
+
headers.host,
|
|
58
67
|
proxyOpts.proxyTunnelPath || '',
|
|
59
|
-
|
|
68
|
+
headers['proxy-authorization'] || ''
|
|
60
69
|
].join(':');
|
|
61
70
|
}
|
|
62
71
|
return [
|
|
72
|
+
getOrigin(req),
|
|
63
73
|
options.servername,
|
|
64
74
|
options.host,
|
|
65
75
|
options.port || '',
|
|
@@ -222,35 +232,35 @@ function getSocket(options, callback, req) {
|
|
|
222
232
|
}
|
|
223
233
|
|
|
224
234
|
function getClient(req, socket, name, callback) {
|
|
225
|
-
var origin = (req.useHttpH2 ? 'http' : 'https') + '://' + req.headers.host;
|
|
226
235
|
var handleCallback = function(err, clt) {
|
|
227
236
|
if (callback) {
|
|
228
237
|
clearTimeout(timer);
|
|
229
238
|
if (err || !clt) {
|
|
230
|
-
|
|
239
|
+
closeClose(client, name);
|
|
231
240
|
socket.destroy();
|
|
232
|
-
client.close();
|
|
233
241
|
}
|
|
234
242
|
callback(clt);
|
|
235
243
|
callback = timer = null;
|
|
236
244
|
}
|
|
237
245
|
};
|
|
238
246
|
var timer = setTimeout(handleCallback, REQ_TIMEOUT);
|
|
239
|
-
var client = http2.connect(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
});
|
|
247
|
+
var client = http2.connect(getOrigin(req), util.setRejectUnauthorized(req, {
|
|
248
|
+
settings: H2_SETTINGS,
|
|
249
|
+
maxSessionMemory: MSM,
|
|
250
|
+
peerMaxConcurrentStreams: PMCS,
|
|
251
|
+
streamResetBurst: RESET_BURST,
|
|
252
|
+
streamResetRate: RESET_RATE,
|
|
253
|
+
createConnection: function () {
|
|
254
|
+
return socket;
|
|
255
|
+
}
|
|
256
|
+
}), function() {
|
|
257
|
+
handleCallback(null, client);
|
|
258
|
+
});
|
|
252
259
|
clients[name] = client;
|
|
253
260
|
client._updateTime = Date.now();
|
|
261
|
+
client.once('goaway', function() {
|
|
262
|
+
closeClose(client, name);
|
|
263
|
+
});
|
|
254
264
|
onClose(client, handleCallback);
|
|
255
265
|
onClose(socket, handleCallback);
|
|
256
266
|
return client;
|
|
@@ -261,11 +271,6 @@ function requestH2(client, req, res, callback) {
|
|
|
261
271
|
return;
|
|
262
272
|
}
|
|
263
273
|
var headers = util.formatH2Headers(req.headers);
|
|
264
|
-
delete req.headers.connection;
|
|
265
|
-
delete req.headers['keep-alive'];
|
|
266
|
-
delete req.headers['http2-settings'];
|
|
267
|
-
delete req.headers['proxy-connection'];
|
|
268
|
-
delete req.headers['transfer-encoding'];
|
|
269
274
|
var options = req.options;
|
|
270
275
|
var responsed;
|
|
271
276
|
headers[':path'] = options.path;
|
|
@@ -327,8 +332,10 @@ function requestH2(client, req, res, callback) {
|
|
|
327
332
|
});
|
|
328
333
|
req.pipe(h2Session);
|
|
329
334
|
} catch (e) {
|
|
330
|
-
!responsed
|
|
331
|
-
|
|
335
|
+
if (!responsed) {
|
|
336
|
+
callback();
|
|
337
|
+
responsed = true;
|
|
338
|
+
}
|
|
332
339
|
}
|
|
333
340
|
}
|
|
334
341
|
|
|
@@ -380,12 +387,16 @@ function checkTlsError(err) {
|
|
|
380
387
|
return typeof code === 'string' && (code.indexOf('ERR_TLS_') === 0 || code.indexOf('ERR_SSL_') === 0);
|
|
381
388
|
}
|
|
382
389
|
|
|
390
|
+
function isClosed(client) {
|
|
391
|
+
return client.destroyed || client.closed;
|
|
392
|
+
}
|
|
393
|
+
|
|
383
394
|
exports.request = function (req, res, callback) {
|
|
384
395
|
var options = req.useH2 && req.options;
|
|
385
396
|
if (!options || options.isPlugin || req._isInternalProxy) {
|
|
386
397
|
return callback();
|
|
387
398
|
}
|
|
388
|
-
var key = getKey(options);
|
|
399
|
+
var key = getKey(options, req);
|
|
389
400
|
var reqId = req.disable.keepH2Session ? '' : req._h2ReqId;
|
|
390
401
|
var name = (reqId || req.clientIp) + '\n' + key;
|
|
391
402
|
var client = clients[name];
|
|
@@ -395,13 +406,21 @@ exports.request = function (req, res, callback) {
|
|
|
395
406
|
name = name + '\n' + client.curIndex;
|
|
396
407
|
client.curIndex = ++client.curIndex % CONCURRENT;
|
|
397
408
|
client = clients[name];
|
|
409
|
+
if (client && isClosed(client)) {
|
|
410
|
+
delete clients[name];
|
|
411
|
+
client = null;
|
|
412
|
+
}
|
|
398
413
|
} else {
|
|
399
414
|
client.curIndex = 1;
|
|
400
415
|
}
|
|
401
416
|
}
|
|
402
417
|
if (client) {
|
|
403
|
-
|
|
404
|
-
|
|
418
|
+
if (!isClosed(client)) {
|
|
419
|
+
client._updateTime = Date.now();
|
|
420
|
+
return requestH2(client, req, res, callback);
|
|
421
|
+
}
|
|
422
|
+
delete clients[name];
|
|
423
|
+
client = null;
|
|
405
424
|
}
|
|
406
425
|
}
|
|
407
426
|
var time = notH2.peek(key);
|
package/lib/https/index.js
CHANGED
|
@@ -1177,6 +1177,12 @@ module.exports = function (socket, next, isWebPort) {
|
|
|
1177
1177
|
headersStr = chunk && chunk.toString();
|
|
1178
1178
|
var isHttp = chunk && HTTP_RE.test(headersStr);
|
|
1179
1179
|
var statusLine = isHttp && RegExp['$&'];
|
|
1180
|
+
var handlePipe = function() {
|
|
1181
|
+
reqSocket.on('error', destroy);
|
|
1182
|
+
reqSocket.write(chunk);
|
|
1183
|
+
reqSocket.pipe(socket).pipe(reqSocket);
|
|
1184
|
+
socket.resume();
|
|
1185
|
+
};
|
|
1180
1186
|
if (isHttp && CONNECT_RE.test(RegExp.$1)) {
|
|
1181
1187
|
chunk = addClientInfo(socket, chunk, statusLine, clientIp, clientPort);
|
|
1182
1188
|
util.connect(
|
|
@@ -1189,10 +1195,7 @@ module.exports = function (socket, next, isWebPort) {
|
|
|
1189
1195
|
if (err || socket._hasError) {
|
|
1190
1196
|
return destroy(err);
|
|
1191
1197
|
}
|
|
1192
|
-
|
|
1193
|
-
reqSocket.write(chunk);
|
|
1194
|
-
reqSocket.pipe(socket).pipe(reqSocket);
|
|
1195
|
-
socket.resume();
|
|
1198
|
+
handlePipe();
|
|
1196
1199
|
}
|
|
1197
1200
|
);
|
|
1198
1201
|
return;
|
|
@@ -1248,12 +1251,10 @@ module.exports = function (socket, next, isWebPort) {
|
|
|
1248
1251
|
if (err || socket._hasError) {
|
|
1249
1252
|
return destroy(err);
|
|
1250
1253
|
}
|
|
1251
|
-
reqSocket.
|
|
1252
|
-
socket._w2TunnelKey =
|
|
1253
|
-
tunnelTmplData.set(
|
|
1254
|
-
|
|
1255
|
-
reqSocket.pipe(socket).pipe(reqSocket);
|
|
1256
|
-
socket.resume();
|
|
1254
|
+
var key = reqSocket.localPort + ':' + reqSocket.remotePort;
|
|
1255
|
+
socket._w2TunnelKey = key;
|
|
1256
|
+
tunnelTmplData.set(key, getTunnelData(socket, clientIp, clientPort, isHttpH2));
|
|
1257
|
+
handlePipe();
|
|
1257
1258
|
}
|
|
1258
1259
|
);
|
|
1259
1260
|
};
|
package/lib/inspectors/data.js
CHANGED
|
@@ -419,7 +419,7 @@ function emitDataEvents(req, res, proxy) {
|
|
|
419
419
|
}
|
|
420
420
|
|
|
421
421
|
req.once('dest', function (_req) {
|
|
422
|
-
|
|
422
|
+
util.onWriteEnd(_req, function () {
|
|
423
423
|
if (!requestTime) {
|
|
424
424
|
setRequestTime(Date.now());
|
|
425
425
|
}
|
|
@@ -441,15 +441,19 @@ function emitDataEvents(req, res, proxy) {
|
|
|
441
441
|
req.once('error', handleError);
|
|
442
442
|
res.once('error', handleError);
|
|
443
443
|
res.once('close', handleError);
|
|
444
|
-
|
|
444
|
+
util.onWriteEnd(res, setEndTime);
|
|
445
445
|
|
|
446
446
|
function handleError(err) {
|
|
447
447
|
req._hasError = true;
|
|
448
448
|
util.clearDelayTimer(req);
|
|
449
|
-
if (!data || endTime || data.endTime
|
|
449
|
+
if (!data || endTime || data.endTime) {
|
|
450
450
|
return;
|
|
451
451
|
}
|
|
452
|
+
// 触发 endTime
|
|
452
453
|
!endTime && setEndTime();
|
|
454
|
+
if (data.responseTime && !err) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
453
457
|
delete data.abort;
|
|
454
458
|
// 不能乱优化,跟 var msg = err && err.message; if (msg !== 'Aborted' && msg !== 'aborted') 不一样
|
|
455
459
|
if (err && err.message !== 'Aborted' && err.message !== 'aborted') {
|
package/lib/inspectors/log.js
CHANGED
|
@@ -7,7 +7,6 @@ var logScriptFile = path.join(config.ASSESTS_PATH, 'js/log.js');
|
|
|
7
7
|
var logScript = fs.readFileSync(logScriptFile, { encoding: 'utf8' });
|
|
8
8
|
var logScriptTag = '<script>' + logScript + '</script>\r\n';
|
|
9
9
|
var logHtmlScript = '<!DOCTYPE html>\r\n' + logScriptTag;
|
|
10
|
-
var LOG_ID_RE = /^log:\/\/(\{[^\s]{1,36}\}|[^/\\{}()<>\s]{1,36})$/;
|
|
11
10
|
|
|
12
11
|
function wrapScript(script, isHtml) {
|
|
13
12
|
return isHtml
|
|
@@ -53,39 +52,21 @@ module.exports = function (req, res) {
|
|
|
53
52
|
transform._transform = function (chunk, _, callback) {
|
|
54
53
|
if (!added) {
|
|
55
54
|
added = true;
|
|
56
|
-
var logId =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
} catch (e) {}
|
|
67
|
-
}
|
|
68
|
-
util.getRuleValue(
|
|
69
|
-
logId && !isValue ? null : log,
|
|
70
|
-
function (script) {
|
|
71
|
-
topScript = topScript.replace('$LOG_ID', logId);
|
|
72
|
-
var buf = [util.toBuffer(topScript)];
|
|
73
|
-
userScript = script || null;
|
|
74
|
-
if (userScript) {
|
|
75
|
-
userScript = util.toBuffer(wrapScript(userScript, isHtml));
|
|
76
|
-
if (isHtml || !chunk) {
|
|
77
|
-
buf.push(userScript);
|
|
78
|
-
userScript = null;
|
|
79
|
-
}
|
|
55
|
+
var logId = util.removeProtocol(log.matcher, true);
|
|
56
|
+
util.getRuleValue(!logId || /^[^/\\{}()<>]+$/.test(logId) ? null : log, function (script) {
|
|
57
|
+
topScript = topScript.replace('$LOG_ID', logId && util.encodeURIComponent(logId));
|
|
58
|
+
var buf = [util.toBuffer(topScript)];
|
|
59
|
+
userScript = script || null;
|
|
60
|
+
if (userScript) {
|
|
61
|
+
userScript = util.toBuffer(wrapScript(userScript, isHtml));
|
|
62
|
+
if (isHtml || !chunk) {
|
|
63
|
+
buf.push(userScript);
|
|
64
|
+
userScript = null;
|
|
80
65
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
null,
|
|
86
|
-
null,
|
|
87
|
-
req
|
|
88
|
-
);
|
|
66
|
+
}
|
|
67
|
+
chunk && buf.push(chunk);
|
|
68
|
+
callback(null, Buffer.concat(buf));
|
|
69
|
+
}, null, null, null, req);
|
|
89
70
|
} else {
|
|
90
71
|
callback(null, chunk || userScript);
|
|
91
72
|
}
|
package/lib/inspectors/res.js
CHANGED
|
@@ -798,7 +798,7 @@ module.exports = function (req, res, next) {
|
|
|
798
798
|
var useFrames;
|
|
799
799
|
res.headers = req.resHeaders = headers;
|
|
800
800
|
res._originEncoding = headers['content-encoding'];
|
|
801
|
-
req.statusCode = _res.statusCode;
|
|
801
|
+
req.statusCode = res.statusCode = _res.statusCode;
|
|
802
802
|
if (_res.rawHeaderNames) {
|
|
803
803
|
res.rawHeaderNames = _res.rawHeaderNames;
|
|
804
804
|
} else {
|
|
@@ -1243,7 +1243,7 @@ module.exports = function (req, res, next) {
|
|
|
1243
1243
|
_res.statusCode,
|
|
1244
1244
|
formatHeaders(curHeaders, rawNames)
|
|
1245
1245
|
);
|
|
1246
|
-
util.
|
|
1246
|
+
util.onReadEnd(_res, function () {
|
|
1247
1247
|
var trailers = _res.trailers;
|
|
1248
1248
|
if (
|
|
1249
1249
|
!res.chunkedEncoding ||
|
package/lib/inspectors/rules.js
CHANGED
|
@@ -61,6 +61,15 @@ function setupRules(req, next) {
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
function handleCodec(socket, enTrans, deTrans, handleError) {
|
|
65
|
+
enTrans.on('error', handleError);
|
|
66
|
+
deTrans.on('error', handleError);
|
|
67
|
+
deTrans.once('end', function() {
|
|
68
|
+
util.endSocket(socket);
|
|
69
|
+
});
|
|
70
|
+
enTrans.pipe(socket).pipe(deTrans);
|
|
71
|
+
}
|
|
72
|
+
|
|
64
73
|
function getDecoder(obj) {
|
|
65
74
|
return function (socket, callback) {
|
|
66
75
|
var encoding = obj._originEncoding;
|
|
@@ -97,9 +106,7 @@ function getDecoder(obj) {
|
|
|
97
106
|
delete obj.headers['content-length'];
|
|
98
107
|
var enTrans = getEncodeTransform();
|
|
99
108
|
var deTrans = getDecodeTransform();
|
|
100
|
-
|
|
101
|
-
enTrans.on('error', handleError);
|
|
102
|
-
deTrans.on('error', handleError);
|
|
109
|
+
handleCodec(socket, enTrans, deTrans, handleError);
|
|
103
110
|
if (decoder) {
|
|
104
111
|
decoder.pipe(enTrans);
|
|
105
112
|
} else {
|
|
@@ -127,9 +134,7 @@ function getEncoder(obj, req) {
|
|
|
127
134
|
delete obj.headers['content-length'];
|
|
128
135
|
var enTrans = getEncodeTransform();
|
|
129
136
|
var deTrans = getDecodeTransform();
|
|
130
|
-
|
|
131
|
-
deTrans.on('error', handleError);
|
|
132
|
-
enTrans.pipe(socket).pipe(deTrans);
|
|
137
|
+
handleCodec(socket, enTrans, deTrans, handleError);
|
|
133
138
|
socket = enTrans;
|
|
134
139
|
if (encoder) {
|
|
135
140
|
deTrans.pipe(encoder);
|
package/lib/inspectors/weinre.js
CHANGED
|
@@ -36,7 +36,7 @@ module.exports = function (req, res) {
|
|
|
36
36
|
}
|
|
37
37
|
!util.isEnable(req, 'keepAllCSP') && util.disableCSP(_res.headers);
|
|
38
38
|
!req._customCache && util.disableResStore(_res.headers);
|
|
39
|
-
var name = util.
|
|
39
|
+
var name = util.removeProtocol(weinre.matcher, true);
|
|
40
40
|
var transform = new Transform();
|
|
41
41
|
transform._transform = function (chunk, _, callback) {
|
|
42
42
|
if (!chunk) {
|