whistle 2.10.9 → 2.10.11
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 +3 -1
- package/bin/util.js +1 -1
- package/bin/whistle.js +5 -0
- package/biz/webui/cgi-bin/get-data.js +0 -1
- package/biz/webui/cgi-bin/init.js +0 -1
- package/biz/webui/cgi-bin/status.js +2 -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/index.js +10 -12
- package/lib/config.js +25 -79
- package/lib/handlers/file-proxy.js +1 -1
- package/lib/https/ca.js +18 -17
- package/lib/https/h2.js +54 -35
- package/lib/https/index.js +124 -107
- package/lib/inspectors/data.js +9 -3
- package/lib/inspectors/log.js +14 -33
- package/lib/inspectors/req.js +3 -6
- package/lib/inspectors/res.js +2 -5
- package/lib/inspectors/rules.js +11 -6
- package/lib/inspectors/weinre.js +1 -1
- package/lib/plugins/index.js +45 -54
- package/lib/plugins/load-plugin.js +31 -40
- package/lib/rules/dns.js +1 -1
- package/lib/rules/index.js +3 -1
- package/lib/rules/protocols.js +2 -4
- package/lib/rules/rules.js +3 -5
- package/lib/rules/util.js +2 -7
- package/lib/service/compose-data.js +2 -1
- package/lib/service/composer.js +12 -10
- package/lib/service/service.js +3 -5
- package/lib/socket-mgr.js +5 -2
- package/lib/tunnel.js +9 -5
- package/lib/util/common.js +190 -37
- 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 +48 -69
- 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 +4 -4
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/index.js
CHANGED
|
@@ -8,7 +8,6 @@ var os = require('os');
|
|
|
8
8
|
var assert = require('assert');
|
|
9
9
|
var common = require('./lib/util/common');
|
|
10
10
|
|
|
11
|
-
var ver = process.version.substring(1).split('.');
|
|
12
11
|
var PROD_RE = /(^|\|)prod(uction)?($|\|)/;
|
|
13
12
|
var noop = function () {};
|
|
14
13
|
var state = {};
|
|
@@ -16,21 +15,20 @@ var INTERVAL = 1000;
|
|
|
16
15
|
var TIMEOUT = 10000;
|
|
17
16
|
var MASTER_TIMEOUT = 12000;
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
}
|
|
18
|
+
var connect = net.Socket.prototype.connect;
|
|
19
|
+
if (typeof connect === 'function') {
|
|
20
|
+
//fix: Node v7.7.0+引入的 `"listener" argument must be a function` 问题
|
|
21
|
+
net.Socket.prototype.connect = function (options, cb) {
|
|
22
|
+
if (options && typeof options === 'object' && typeof cb !== 'function') {
|
|
23
|
+
return connect.call(this, options, null);
|
|
24
|
+
}
|
|
25
|
+
return connect.apply(this, arguments);
|
|
26
|
+
};
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
var env = process.env || '';
|
|
33
30
|
env.WHISTLE_ROOT = __dirname;
|
|
31
|
+
env.PFORK_MAX_HTTP_HEADER_SIZE = 1024 * 1024;
|
|
34
32
|
if (typeof tls.checkServerIdentity == 'function') {
|
|
35
33
|
var checkServerIdentity = tls.checkServerIdentity;
|
|
36
34
|
tls.checkServerIdentity = function () {
|
package/lib/config.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
var path = require('path');
|
|
2
|
-
var http = require('http');
|
|
3
2
|
var fs = require('fs');
|
|
4
3
|
var net = require('net');
|
|
5
4
|
var dns = require('dns');
|
|
6
5
|
var qs = require('querystring');
|
|
7
6
|
var LRU = require('lru-cache');
|
|
8
|
-
var
|
|
9
|
-
var
|
|
7
|
+
var HttpAgent = require('http').Agent;
|
|
8
|
+
var HttpsAgent = require('https').Agent;
|
|
10
9
|
var parseUrl = require('./util/parse-url-safe');
|
|
11
10
|
var fse = require('fs-extra2');
|
|
12
11
|
var extend = require('extend');
|
|
@@ -25,14 +24,11 @@ var CMD_RE = /^[\w]{1,12}(?:\s+-g)?$/;
|
|
|
25
24
|
var httpsAgents = new LRU({ max: 360 });
|
|
26
25
|
var socksAgents = new LRU({ max: 100 });
|
|
27
26
|
var version = process.version.substring(1).split('.');
|
|
28
|
-
var
|
|
29
|
-
var useVerbatim = version[0] >= 17;
|
|
30
|
-
var hasIPv6First = version[0] >= 22 && version[1] >= 1;
|
|
27
|
+
var hasIPv6First = (version[0] === 22 && version[1] >= 1) || version[0] > 22;
|
|
31
28
|
var hasDnsOrder = typeof dns.setDefaultResultOrder === 'function';
|
|
32
29
|
var uid = Date.now().toString(16) + (process.pid * 10000 + Math.floor(Math.random() * 10000)).toString(16);
|
|
33
30
|
var IPV4_RE = /^([\d.]+)(:\d{1,5})?$/;
|
|
34
31
|
var IPV6_RE = /^\[([\w:]+)\](:\d{1,5})?$/;
|
|
35
|
-
var noop = function () {};
|
|
36
32
|
var DATA_KEY_RE = /^(clientip|clientid|tunneldata)([=.])([\w.-]+)$/i;
|
|
37
33
|
var DNS_ORDERS = ['ipv4First', 'ipv4first', 'ipv6first', 'ipv6First', 'verbatim'];
|
|
38
34
|
var LOCAL_UI_HOST_LIST = [
|
|
@@ -61,18 +57,12 @@ var variableProperties = [
|
|
|
61
57
|
'disableInstaller'
|
|
62
58
|
];
|
|
63
59
|
if (hasDnsOrder) {
|
|
64
|
-
|
|
65
|
-
config.verbatim = 2;
|
|
66
|
-
} else {
|
|
67
|
-
config.verbatim = 1;
|
|
68
|
-
}
|
|
60
|
+
config.verbatim = hasIPv6First ? 2 : 1;
|
|
69
61
|
}
|
|
70
62
|
|
|
71
63
|
config.appName = common.upperFirst(config.name);
|
|
72
|
-
config.defaultDnsOrder = useVerbatim ? 1 : 2;
|
|
73
64
|
config.uid = uid;
|
|
74
65
|
config.rejectUnauthorized = false;
|
|
75
|
-
config.enableH2 = version[0] > 12; // 支持 HTTP2 要求的最低 Node 版本
|
|
76
66
|
config.ASSESTS_PATH = path.join(__dirname, '../assets');
|
|
77
67
|
config.WHISTLE_POLICY_HEADER = 'x-whistle-policy';
|
|
78
68
|
config.CLIENT_IP_HEADER = common.CLIENT_IP_HEADER;
|
|
@@ -119,16 +109,18 @@ config.PLUGIN_HOOKS = {
|
|
|
119
109
|
|
|
120
110
|
var KEEP_ALIVE_MSECS = 10000;
|
|
121
111
|
var CONN_TIMEOUT = common.CONN_TIMEOUT;
|
|
122
|
-
var DNS_ORDERS_OPTS = ['', '
|
|
112
|
+
var DNS_ORDERS_OPTS = ['', 'ipv4first', 'ipv4first', hasIPv6First ? 'ipv6first' : '', '', 'verbatim'];
|
|
123
113
|
var DOMAIN_STAR_RE = /([*~]+)(\\.)?/g;
|
|
124
114
|
|
|
125
115
|
config.CONN_TIMEOUT = CONN_TIMEOUT;
|
|
126
116
|
|
|
127
117
|
var getWhistlePath = common.getWhistlePath;
|
|
128
118
|
var getHostPort = common.getHostPort;
|
|
119
|
+
var WHISTLE_PATH = getWhistlePath();
|
|
129
120
|
|
|
130
|
-
var ALL_WHISTLES_PATH = path.join(
|
|
121
|
+
var ALL_WHISTLES_PATH = path.join(WHISTLE_PATH, 'all_whistles');
|
|
131
122
|
|
|
123
|
+
config.WHISTLE_PATH = WHISTLE_PATH;
|
|
132
124
|
|
|
133
125
|
function domainToRegExp(_, star, dot) {
|
|
134
126
|
var len = star.length;
|
|
@@ -143,16 +135,16 @@ function domainToRegExp(_, star, dot) {
|
|
|
143
135
|
}
|
|
144
136
|
|
|
145
137
|
function getDataDir(dirname) {
|
|
146
|
-
var dir = path.join(
|
|
138
|
+
var dir = path.join(WHISTLE_PATH, dirname || '.' + config.name);
|
|
147
139
|
fse.ensureDirSync(dir);
|
|
148
140
|
return dir;
|
|
149
141
|
}
|
|
150
142
|
|
|
151
143
|
config.baseDir = getDataDir();
|
|
152
|
-
config.CUSTOM_PLUGIN_PATH = path.join(
|
|
153
|
-
config.CUSTOM_CERTS_DIR = path.resolve(
|
|
154
|
-
config.SAVED_SESSIONS_PATH = path.resolve(
|
|
155
|
-
config.DEV_PLUGINS_PATH = path.resolve(
|
|
144
|
+
config.CUSTOM_PLUGIN_PATH = path.join(WHISTLE_PATH, 'custom_plugins');
|
|
145
|
+
config.CUSTOM_CERTS_DIR = path.resolve(WHISTLE_PATH, 'custom_certs');
|
|
146
|
+
config.SAVED_SESSIONS_PATH = path.resolve(WHISTLE_PATH, 'saved_sessions');
|
|
147
|
+
config.DEV_PLUGINS_PATH = path.resolve(WHISTLE_PATH, 'dev_plugins');
|
|
156
148
|
config.rulesDir = path.join(config.baseDir, 'rules');
|
|
157
149
|
config.valuesDir = path.join(config.baseDir, 'values');
|
|
158
150
|
config.propertiesDir = path.join(config.baseDir, 'properties');
|
|
@@ -173,55 +165,6 @@ try {
|
|
|
173
165
|
|
|
174
166
|
exports.getWhistlePath = getWhistlePath;
|
|
175
167
|
exports.getDataDir = getDataDir;
|
|
176
|
-
var async_id_symbol;
|
|
177
|
-
if (version[0] < 16) {
|
|
178
|
-
try {
|
|
179
|
-
async_id_symbol = process.binding('async_wrap').async_id_symbol;
|
|
180
|
-
} catch (e) {}
|
|
181
|
-
}
|
|
182
|
-
var emptyHandle = {
|
|
183
|
-
asyncReset: noop,
|
|
184
|
-
getAsyncId: noop
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
function createAgent(agentConfig, https) {
|
|
188
|
-
var agent = new (https ? httpsAgent : httpAgent)(agentConfig);
|
|
189
|
-
if (async_id_symbol) {
|
|
190
|
-
var addRequest = agent.addRequest;
|
|
191
|
-
agent.addRequest = function (req, options) {
|
|
192
|
-
// fix: https://github.com/nodejs/node/issues/13539
|
|
193
|
-
var freeSockets = this.freeSockets[this.getName(options)];
|
|
194
|
-
if (freeSockets && freeSockets.length) {
|
|
195
|
-
var socket = freeSockets[0];
|
|
196
|
-
var handle = socket._handle;
|
|
197
|
-
if (!handle) {
|
|
198
|
-
socket._handle = emptyHandle;
|
|
199
|
-
} else if (typeof handle.asyncReset !== 'function') {
|
|
200
|
-
handle.asyncReset = noop;
|
|
201
|
-
}
|
|
202
|
-
var originalRef = socket.ref;
|
|
203
|
-
socket.ref = function () {
|
|
204
|
-
socket.ref = originalRef;
|
|
205
|
-
if (socket._handle === emptyHandle) {
|
|
206
|
-
delete socket._handle;
|
|
207
|
-
} else if (socket._handle.asyncReset === noop) {
|
|
208
|
-
delete socket._handle.asyncReset;
|
|
209
|
-
}
|
|
210
|
-
socket.ref();
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
var onSocket = req.onSocket;
|
|
214
|
-
req.onSocket = function (socket) {
|
|
215
|
-
try {
|
|
216
|
-
socket[async_id_symbol] = socket._handle.getAsyncId();
|
|
217
|
-
} catch (e) {}
|
|
218
|
-
onSocket.apply(this, arguments);
|
|
219
|
-
};
|
|
220
|
-
addRequest.apply(this, arguments);
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
return agent;
|
|
224
|
-
}
|
|
225
168
|
|
|
226
169
|
function getHttpsAgent(options, reqOpts) {
|
|
227
170
|
var key = getCacheKey(options);
|
|
@@ -305,13 +248,16 @@ function getAuths(_url) {
|
|
|
305
248
|
|
|
306
249
|
exports.getAuths = getAuths;
|
|
307
250
|
|
|
251
|
+
function getDnsOrder(order) {
|
|
252
|
+
return order == 1 ? 2 : order;
|
|
253
|
+
}
|
|
254
|
+
|
|
308
255
|
function setDefaultResultOrder(order) {
|
|
309
256
|
if (!hasDnsOrder) {
|
|
310
257
|
return;
|
|
311
258
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return;
|
|
259
|
+
if (DNS_ORDERS_OPTS.indexOf(order) === -1) {
|
|
260
|
+
order = DNS_ORDERS_OPTS[order] || DNS_ORDERS_OPTS[1];
|
|
315
261
|
}
|
|
316
262
|
config.ipv4First = false;
|
|
317
263
|
config.ipv6First = false;
|
|
@@ -321,10 +267,10 @@ function setDefaultResultOrder(order) {
|
|
|
321
267
|
config.ipv4First = true;
|
|
322
268
|
}
|
|
323
269
|
try {
|
|
324
|
-
if (dns.getDefaultResultOrder() !== order) {
|
|
270
|
+
if (!dns.getDefaultResultOrder || dns.getDefaultResultOrder() !== order) {
|
|
325
271
|
dns.setDefaultResultOrder(order);
|
|
326
272
|
}
|
|
327
|
-
config.dnsOrder = DNS_ORDERS_OPTS.indexOf(order);
|
|
273
|
+
config.dnsOrder = getDnsOrder(DNS_ORDERS_OPTS.indexOf(order));
|
|
328
274
|
return true;
|
|
329
275
|
} catch (e) {}
|
|
330
276
|
}
|
|
@@ -1096,10 +1042,10 @@ exports.extend = function (newConf) {
|
|
|
1096
1042
|
keepAliveMsecs: KEEP_ALIVE_MSECS,
|
|
1097
1043
|
maxFreeSockets: 0
|
|
1098
1044
|
};
|
|
1099
|
-
// node 11
|
|
1100
|
-
disableAgent = !newAgentConf && !useAgent
|
|
1101
|
-
config.httpAgent = disableAgent ? false :
|
|
1102
|
-
config.httpsAgent = disableAgent ? false :
|
|
1045
|
+
// node 11及以上版本缓存连接有问题,默认禁掉
|
|
1046
|
+
var disableAgent = !newAgentConf && !useAgent;
|
|
1047
|
+
config.httpAgent = disableAgent ? false : new HttpAgent(agentConfig);
|
|
1048
|
+
config.httpsAgent = disableAgent ? false : new HttpsAgent(agentConfig);
|
|
1103
1049
|
config.getSocksAgent = function (options) {
|
|
1104
1050
|
var key = getCacheKey(options, true);
|
|
1105
1051
|
var agent = socksAgents.get(key);
|
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
|
|
|
@@ -690,7 +691,7 @@ exports.getHttp2Server = function (listener, callback) {
|
|
|
690
691
|
};
|
|
691
692
|
|
|
692
693
|
exports.getSNIServer = function (listener, callback, disableH2, requestCert) {
|
|
693
|
-
var enableH2 =
|
|
694
|
+
var enableH2 = !disableH2;
|
|
694
695
|
var name = (enableH2 ? 'h2Sni' : 'sni') + (requestCert ? 'WithCert' : '');
|
|
695
696
|
var curPort = ports[name];
|
|
696
697
|
if (curPort) {
|
package/lib/https/h2.js
CHANGED
|
@@ -5,7 +5,7 @@ var sockx = require('sockx');
|
|
|
5
5
|
var util = require('../util');
|
|
6
6
|
var config = require('../config');
|
|
7
7
|
var extend = require('extend');
|
|
8
|
-
var http2 =
|
|
8
|
+
var http2 = require('http2');
|
|
9
9
|
|
|
10
10
|
var SUPPORTED_PROTOS = ['h2', 'http/1.1', 'http/1.0'];
|
|
11
11
|
var H2_SETTINGS = { enablePush: false };
|
|
@@ -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);
|