whistle 2.10.1 → 2.10.3
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 +44 -27
- package/assets/menu.html +2 -0
- package/assets/modal.html +2 -0
- package/assets/tab.html +3 -3
- package/bin/use.js +236 -101
- package/bin/util.js +10 -7
- package/bin/whistle.js +5 -5
- package/biz/webui/cgi-bin/download.js +1 -1
- package/biz/webui/cgi-bin/get-data.js +0 -5
- package/biz/webui/cgi-bin/init.js +0 -2
- package/biz/webui/cgi-bin/rules/clear-dns-cache.js +7 -0
- package/biz/webui/htdocs/index.html +2 -2
- package/biz/webui/htdocs/js/index.js +53 -50
- package/biz/webui/lib/index.js +49 -4
- package/lib/config.js +6 -4
- package/lib/handlers/file-proxy.js +1 -1
- package/lib/https/ca.js +39 -9
- package/lib/https/index.js +109 -114
- package/lib/index.js +1 -5
- package/lib/inspectors/data.js +119 -13
- package/lib/inspectors/log.js +4 -0
- package/lib/inspectors/req.js +5 -3
- package/lib/inspectors/weinre.js +7 -2
- package/lib/plugins/get-plugins-sync.js +15 -11
- package/lib/plugins/get-plugins.js +11 -12
- package/lib/plugins/load-plugin.js +1 -1
- package/lib/plugins/module-paths.js +4 -0
- package/lib/plugins/util.js +8 -0
- package/lib/rules/dns.js +4 -0
- package/lib/rules/index.js +0 -1
- package/lib/rules/protocols.js +3 -9
- package/lib/rules/rules.js +21 -22
- package/lib/service/composer.js +10 -5
- package/lib/{util/log-server.js → service/console-log.js} +37 -34
- package/lib/service/index.js +2 -4
- package/lib/service/service.js +64 -15
- package/lib/service/util.js +3 -42
- package/lib/socket-mgr.js +2 -16
- package/lib/tunnel.js +2 -4
- package/lib/upgrade.js +1 -1
- package/lib/util/common.js +115 -17
- package/lib/util/index.js +48 -40
- package/package.json +2 -2
- package/biz/webui/cgi-bin/import-remote.js +0 -37
- package/biz/webui/cgi-bin/log/set.js +0 -7
- package/lib/util/parse-query.js +0 -35
package/biz/webui/lib/index.js
CHANGED
|
@@ -20,6 +20,7 @@ var common = require('../../../lib/util/common');
|
|
|
20
20
|
var getWorker = require('../../../lib/plugins/util').getWorker;
|
|
21
21
|
var loadAuthPlugins = require('../../../lib/plugins').loadAuthPlugins;
|
|
22
22
|
var parseUrl = require('../../../lib/util/parse-url-safe');
|
|
23
|
+
var loadService = require('../../../lib/service');
|
|
23
24
|
|
|
24
25
|
var sendError = cgiUtil.sendError;
|
|
25
26
|
var sendGzipText = cgiUtil.sendGzipText;
|
|
@@ -55,12 +56,32 @@ var MENU_URL = '???_WHISTLE_PLUGIN_EXT_CONTEXT_MENU_' + config.port + '???';
|
|
|
55
56
|
var INSPECTOR_URL = '???_WHISTLE_PLUGIN_INSPECTOR_TAB_' + config.port + '???';
|
|
56
57
|
var KEY_RE_G = /\${[^{}\s]+}|{\S+}/g;
|
|
57
58
|
var COMMENT_RE = /#[^\r\n]*$/mg;
|
|
59
|
+
var MAX_LEN = 1024 * 1024 * 6;
|
|
60
|
+
var HTTP_RE = /https?:\/\/\S/i;
|
|
61
|
+
|
|
62
|
+
function hasLogin() {
|
|
63
|
+
return config.username && config.password;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isTempFile(query) {
|
|
67
|
+
var files = query.files;
|
|
68
|
+
if (files && typeof files === 'string') {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return common.getTempFile(query.filename);
|
|
72
|
+
}
|
|
58
73
|
|
|
59
74
|
function sendToService(req, res) {
|
|
60
|
-
|
|
75
|
+
if (!hasLogin() && req.path === '/cgi-bin/temp/get' && !isTempFile(req.query)) {
|
|
76
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
77
|
+
res.end('{"ec":0,"value":"","forbidden":true}');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
loadService(function(err, options) {
|
|
61
81
|
if (err) {
|
|
62
82
|
common.sendRes(res, 500, err.stack || err);
|
|
63
83
|
} else {
|
|
84
|
+
req.headers[common.WHISTLE_UID_HEADER] = config.uid;
|
|
64
85
|
util.transformReq(req, res, options.port);
|
|
65
86
|
}
|
|
66
87
|
});
|
|
@@ -390,13 +411,37 @@ function cgiHandler(req, res) {
|
|
|
390
411
|
handleResponse();
|
|
391
412
|
});
|
|
392
413
|
}
|
|
414
|
+
app.get('/cgi-bin/import-remote', function(req, res) {
|
|
415
|
+
var url = req.query.url;
|
|
416
|
+
if (HTTP_RE.test(url)) {
|
|
417
|
+
util.request({
|
|
418
|
+
url: url,
|
|
419
|
+
maxLength: MAX_LEN
|
|
420
|
+
}, function(err, body, r) {
|
|
421
|
+
if (err) {
|
|
422
|
+
var msg = err.code === 'EEXCEED' ? 'The size of response body exceeds 6MB' : err.message;
|
|
423
|
+
return res.json({ec: 2, em: msg});
|
|
424
|
+
}
|
|
425
|
+
var status = r.statusCode;
|
|
426
|
+
if (status !== 200) {
|
|
427
|
+
var em = status > 200 && status < 400 ? 'No data' : 'Request failed';
|
|
428
|
+
return res.json({ec: 2, em: em + ' (statusCode: ' + status + ')'});
|
|
429
|
+
}
|
|
430
|
+
return res.json({ec: 0, body: body});
|
|
431
|
+
});
|
|
432
|
+
} else if (util.isString(url)) {
|
|
433
|
+
req.url = '/cgi-bin/temp/get?filename=' + encodeURIComponent(url);
|
|
434
|
+
req.query.filename = url;
|
|
435
|
+
sendToService(req, res);
|
|
436
|
+
} else {
|
|
437
|
+
res.json({ec: 400, em: 'Bad url'});
|
|
438
|
+
}
|
|
439
|
+
});
|
|
393
440
|
app.all('/service/*', sendToService);
|
|
394
441
|
app.all('/cgi-bin/service/*', sendToService);
|
|
395
442
|
app.all('/cgi-bin/sessions/*', sendToService);
|
|
443
|
+
app.all('/cgi-bin/log/*', sendToService);
|
|
396
444
|
app.post('/cgi-bin/plugins/install', sendToService);
|
|
397
|
-
app.all('/favicon.ico', function(req, res) {
|
|
398
|
-
res.sendFile(htdocs.getImgFile('favicon.ico'));
|
|
399
|
-
});
|
|
400
445
|
|
|
401
446
|
function readPluginPage(req, res, plugin, html, config) {
|
|
402
447
|
res.type('html');
|
package/lib/config.js
CHANGED
|
@@ -148,15 +148,17 @@ function getDataDir(dirname) {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
config.baseDir = getDataDir();
|
|
151
|
-
config.TEMP_FILES_PATH = path.join(getWhistlePath(), 'temp_files');
|
|
152
151
|
config.CUSTOM_PLUGIN_PATH = path.join(getWhistlePath(), 'custom_plugins');
|
|
153
152
|
config.CUSTOM_CERTS_DIR = path.resolve(getWhistlePath(), 'custom_certs');
|
|
154
153
|
config.SAVED_SESSIONS_PATH = path.resolve(getWhistlePath(), 'saved_sessions');
|
|
155
154
|
config.DEV_PLUGINS_PATH = path.resolve(getWhistlePath(), 'dev_plugins');
|
|
155
|
+
config.rulesDir = path.join(config.baseDir, 'rules');
|
|
156
|
+
config.valuesDir = path.join(config.baseDir, 'values');
|
|
157
|
+
config.propertiesDir = path.join(config.baseDir, 'properties');
|
|
156
158
|
|
|
157
159
|
try {
|
|
158
|
-
fse.ensureDirSync(
|
|
159
|
-
var blankFile = path.join(
|
|
160
|
+
fse.ensureDirSync(common.TEMP_FILES_PATH);
|
|
161
|
+
var blankFile = path.join(common.TEMP_FILES_PATH, 'blank');
|
|
160
162
|
if (!fs.existsSync(blankFile)) {
|
|
161
163
|
fs.writeFileSync(blankFile, '');
|
|
162
164
|
}
|
|
@@ -793,7 +795,7 @@ exports.extend = function (newConf) {
|
|
|
793
795
|
setDefaultResultOrder(m.toLowerCase());
|
|
794
796
|
} else if (m === 'proxyOnly') {
|
|
795
797
|
config.pureProxy = true;
|
|
796
|
-
} else if (m === 'useMultipleRules' || m === 'enableMultipleRules') {
|
|
798
|
+
} else if (m === 'useMultipleRules' || m === 'enableMultipleRules' || m === 'allowMultipleChoice') {
|
|
797
799
|
config.allowMultipleChoice = true;
|
|
798
800
|
} else if (m === 'disableMultipleRules') {
|
|
799
801
|
config.allowMultipleChoice = false;
|
|
@@ -66,7 +66,7 @@ function parseRes(str, rawHeaderNames, fromValue) {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
var headers = str.split(CRLF_RE);
|
|
69
|
-
var statusLine = headers.shift().trim().split(/\s+/
|
|
69
|
+
var statusLine = headers.shift().trim().split(/\s+/);
|
|
70
70
|
headers = util.parseHeaders(headers, rawHeaderNames);
|
|
71
71
|
if (fromValue) {
|
|
72
72
|
delete headers['content-encoding'];
|
package/lib/https/ca.js
CHANGED
|
@@ -15,8 +15,6 @@ var common = require('../util/common');
|
|
|
15
15
|
var rulesUtil = require('../rules/util');
|
|
16
16
|
|
|
17
17
|
var pki = forge.pki;
|
|
18
|
-
var CUR_VERSION = process.version;
|
|
19
|
-
var requiredVersion = parseInt(CUR_VERSION.slice(1), 10) >= 6;
|
|
20
18
|
var HTTPS_DIR = mkdir(path.join(config.getDataDir(), 'certs'));
|
|
21
19
|
var ROOT_NEW_KEY_FILE = path.join(HTTPS_DIR, 'root_new.key');
|
|
22
20
|
var ROOT_NEW_CRT_FILE = path.join(HTTPS_DIR, 'root_new.crt');
|
|
@@ -40,6 +38,7 @@ var customCertCount = 0;
|
|
|
40
38
|
var cachePairs = new LRU({ max: 5120 });
|
|
41
39
|
var certsCache = new LRU({ max: 256 });
|
|
42
40
|
var remoteCerts = new LRU({ max: 1280 });
|
|
41
|
+
var KEY_SIZE = 2048;
|
|
43
42
|
var ILEGAL_CHAR_RE = /[^a-z\d-]/i;
|
|
44
43
|
// https://cloud.tencent.com/developer/ask/sof/58155
|
|
45
44
|
var RANDOM_SERIAL = '/' + Date.now() + '/' + process.pid + '/' + Math.floor(Math.random() * 100);
|
|
@@ -58,12 +57,12 @@ exports.remoteCerts = remoteCerts;
|
|
|
58
57
|
exports.createSecureContext = createSecureContext;
|
|
59
58
|
exports.CUSTOM_CERTS_DIR = CUSTOM_CERTS_DIR;
|
|
60
59
|
|
|
61
|
-
function
|
|
60
|
+
function notRootCa(name) {
|
|
62
61
|
return name !== 'root';
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
function checkFilename(name) {
|
|
66
|
-
return name && !ILLEGAL_PATH_RE.test(name) &&
|
|
65
|
+
return name && !ILLEGAL_PATH_RE.test(name) && notRootCa(name);
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
function resetAllCerts() {
|
|
@@ -84,7 +83,7 @@ if (timer && typeof timer.unref === 'function') {
|
|
|
84
83
|
timer.unref();
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
if (!useNewKey &&
|
|
86
|
+
if (!useNewKey && !checkCertificate()) {
|
|
88
87
|
try {
|
|
89
88
|
fs.unlinkSync(ROOT_KEY_FILE);
|
|
90
89
|
fs.unlinkSync(ROOT_CRT_FILE);
|
|
@@ -108,7 +107,7 @@ function isExpiredCert(crt) {
|
|
|
108
107
|
function checkCertificate() {
|
|
109
108
|
try {
|
|
110
109
|
var crt = pki.certificateFromPem(fs.readFileSync(ROOT_CRT_FILE));
|
|
111
|
-
if (crt.publicKey.n.toString(2).length <
|
|
110
|
+
if (crt.publicKey.n.toString(2).length < KEY_SIZE || isExpiredCert(crt.validity)) {
|
|
112
111
|
return false;
|
|
113
112
|
}
|
|
114
113
|
return /^whistle\.\d+$/.test(getCommonName(crt));
|
|
@@ -178,6 +177,19 @@ exports.hasCustomCerts = function () {
|
|
|
178
177
|
return customCertCount;
|
|
179
178
|
};
|
|
180
179
|
|
|
180
|
+
var rootSkiBytes;
|
|
181
|
+
|
|
182
|
+
function getRootSKI() {
|
|
183
|
+
if (!rootSkiBytes) {
|
|
184
|
+
try {
|
|
185
|
+
rootSkiBytes = ROOT_CRT.generateSubjectKeyIdentifier().getBytes();
|
|
186
|
+
} catch (e) {
|
|
187
|
+
rootSkiBytes = null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return rootSkiBytes;
|
|
191
|
+
}
|
|
192
|
+
|
|
181
193
|
function getCacheCert(hostname) {
|
|
182
194
|
return (
|
|
183
195
|
customPairs[hostname] ||
|
|
@@ -207,6 +219,20 @@ function createSelfCert(hostname) {
|
|
|
207
219
|
]);
|
|
208
220
|
cert.setIssuer(ROOT_CRT.subject.attributes);
|
|
209
221
|
cert.setExtensions([
|
|
222
|
+
{
|
|
223
|
+
name: 'basicConstraints',
|
|
224
|
+
cA: false
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: 'keyUsage',
|
|
228
|
+
digitalSignature: true,
|
|
229
|
+
keyEncipherment: true
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: 'extKeyUsage',
|
|
233
|
+
serverAuth: true,
|
|
234
|
+
clientAuth: true
|
|
235
|
+
},
|
|
210
236
|
{
|
|
211
237
|
name: 'subjectAltName',
|
|
212
238
|
altNames: [
|
|
@@ -220,6 +246,10 @@ function createSelfCert(hostname) {
|
|
|
220
246
|
value: hostname
|
|
221
247
|
}
|
|
222
248
|
]
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: 'authorityKeyIdentifier',
|
|
252
|
+
keyIdentifier: getRootSKI() // Get SKI from ROOT_CRT for authorityKeyIdentifier extension
|
|
223
253
|
}
|
|
224
254
|
]);
|
|
225
255
|
cert.sign(ROOT_KEY, forge.md.sha256.create());
|
|
@@ -265,7 +295,7 @@ function parseAllCustomCerts() {
|
|
|
265
295
|
var validity = info.validity;
|
|
266
296
|
var altNames = info.altNames;
|
|
267
297
|
var dnsName = [];
|
|
268
|
-
var disabled = (
|
|
298
|
+
var disabled = (notRootCa(filename) && rulesUtil.isDisabledCertFile(filename)) || undefined;
|
|
269
299
|
altNames.forEach(function (item) {
|
|
270
300
|
if ((item.type === 2 || item.type === 7) && !pairs[item.value]) {
|
|
271
301
|
if (!disabled) {
|
|
@@ -439,7 +469,7 @@ function createRootCA() {
|
|
|
439
469
|
|
|
440
470
|
function createCACert(opts) {
|
|
441
471
|
opts = opts || {};
|
|
442
|
-
var keys = pki.rsa.generateKeyPair(
|
|
472
|
+
var keys = pki.rsa.generateKeyPair(KEY_SIZE);
|
|
443
473
|
var cert = createCert(keys.publicKey);
|
|
444
474
|
var now = Date.now() + common.padLeft(Math.floor(Math.random() * 1000), 3);
|
|
445
475
|
var attrs = [
|
|
@@ -757,7 +787,7 @@ exports.setActiveCert = function (opts) {
|
|
|
757
787
|
return;
|
|
758
788
|
}
|
|
759
789
|
var filename = opts.filename;
|
|
760
|
-
if (
|
|
790
|
+
if (notRootCa(filename) && allCustomCerts[filename]) {
|
|
761
791
|
rulesUtil.setDisabledCertFile(filename, opts.disabled);
|
|
762
792
|
parseAllCustomCerts();
|
|
763
793
|
}
|
package/lib/https/index.js
CHANGED
|
@@ -207,7 +207,7 @@ function resolveWebsocket(socket, wss) {
|
|
|
207
207
|
};
|
|
208
208
|
|
|
209
209
|
var plugin = pluginMgr.resolveWhistlePlugins(socket);
|
|
210
|
-
|
|
210
|
+
handleEnd(socket);
|
|
211
211
|
pluginMgr.getRules(socket, function (rulesMgr) {
|
|
212
212
|
if (rulesMgr) {
|
|
213
213
|
socket.pluginRules = rulesMgr;
|
|
@@ -394,7 +394,7 @@ function resolveWebsocket(socket, wss) {
|
|
|
394
394
|
proxySocket.on('error', handleProxyError);
|
|
395
395
|
reqSocket = proxySocket;
|
|
396
396
|
proxySocket.on('secureConnect', function () {
|
|
397
|
-
|
|
397
|
+
handleEnd(reqSocket);
|
|
398
398
|
pipeData();
|
|
399
399
|
});
|
|
400
400
|
} catch (e) {
|
|
@@ -403,7 +403,7 @@ function resolveWebsocket(socket, wss) {
|
|
|
403
403
|
return;
|
|
404
404
|
}
|
|
405
405
|
reqSocket = proxySocket;
|
|
406
|
-
|
|
406
|
+
handleEnd(reqSocket);
|
|
407
407
|
pipeData();
|
|
408
408
|
};
|
|
409
409
|
// 对应 internal-proxy 要用直接请求,方便用来穿透 nginx
|
|
@@ -557,7 +557,7 @@ function resolveWebsocket(socket, wss) {
|
|
|
557
557
|
return execCallback(e);
|
|
558
558
|
}
|
|
559
559
|
if (retryConnect) {
|
|
560
|
-
|
|
560
|
+
handleEnd(reqSocket);
|
|
561
561
|
} else {
|
|
562
562
|
retryConnect = function (e) {
|
|
563
563
|
if (!newIp && (newIp = util.getLocalhostIP(e, socket, socket._w2hostname, socket.hostIp))) {
|
|
@@ -621,7 +621,7 @@ function resolveWebsocket(socket, wss) {
|
|
|
621
621
|
var disable = socket.disable;
|
|
622
622
|
if (retryConnect) {
|
|
623
623
|
reqSocket.removeListener('error', retryConnect);
|
|
624
|
-
|
|
624
|
+
handleEnd(reqSocket);
|
|
625
625
|
retryConnect = null;
|
|
626
626
|
}
|
|
627
627
|
clearTimeout(timeout);
|
|
@@ -718,9 +718,10 @@ function resolveWebsocket(socket, wss) {
|
|
|
718
718
|
extend(headers, reqHeaders);
|
|
719
719
|
headers.host = headers.host || host;
|
|
720
720
|
}
|
|
721
|
-
|
|
721
|
+
authObj = auth || authObj;
|
|
722
|
+
auth = util.getAuthBasic(authObj);
|
|
722
723
|
if (auth) {
|
|
723
|
-
headers['authorization'] = auth;
|
|
724
|
+
headers[(authObj.proxy ? 'proxy-' : '') + 'authorization'] = auth;
|
|
724
725
|
}
|
|
725
726
|
var reqRuleData = { headers: headers };
|
|
726
727
|
util.setReqCors(reqRuleData, reqCors);
|
|
@@ -847,7 +848,7 @@ function resolveWebsocket(socket, wss) {
|
|
|
847
848
|
}
|
|
848
849
|
}
|
|
849
850
|
|
|
850
|
-
function
|
|
851
|
+
function handleEnd(socket) {
|
|
851
852
|
return util.onSocketEnd(socket, destroy);
|
|
852
853
|
}
|
|
853
854
|
var destroyed, reqDestroyed, resDestroyed;
|
|
@@ -1080,11 +1081,10 @@ function toHttp1(req, res) {
|
|
|
1080
1081
|
formatRawHeaders(svrRes, isH2)
|
|
1081
1082
|
);
|
|
1082
1083
|
var write = res.write;
|
|
1083
|
-
var handleError = function (e) {
|
|
1084
|
-
e && handleAbort();
|
|
1085
|
-
};
|
|
1086
1084
|
res.write = function (chunk) {
|
|
1087
|
-
return write.call(res, chunk,
|
|
1085
|
+
return write.call(res, chunk, function (e) {
|
|
1086
|
+
e && handleAbort();
|
|
1087
|
+
});
|
|
1088
1088
|
};
|
|
1089
1089
|
svrRes.pipe(res);
|
|
1090
1090
|
res.flushHeaders && res.flushHeaders();
|
|
@@ -1168,152 +1168,147 @@ module.exports = function (socket, next, isWebPort) {
|
|
|
1168
1168
|
|
|
1169
1169
|
util.onSocketEnd(socket, destroy);
|
|
1170
1170
|
|
|
1171
|
-
function abortIfUnavailable(s) {
|
|
1172
|
-
return s.on('error', destroy);
|
|
1173
|
-
}
|
|
1174
1171
|
var clientIp = socket.clientIp;
|
|
1175
1172
|
var clientPort = socket.clientPort;
|
|
1176
|
-
util.readOneChunk(
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
host: LOCALHOST
|
|
1188
|
-
},
|
|
1173
|
+
util.readOneChunk(socket, function (chunk) {
|
|
1174
|
+
headersStr = chunk && chunk.toString();
|
|
1175
|
+
var isHttp = chunk && HTTP_RE.test(headersStr);
|
|
1176
|
+
var statusLine = isHttp && RegExp['$&'];
|
|
1177
|
+
if (isHttp && CONNECT_RE.test(RegExp.$1)) {
|
|
1178
|
+
chunk = addClientInfo(socket, chunk, statusLine, clientIp, clientPort);
|
|
1179
|
+
util.connect(
|
|
1180
|
+
{
|
|
1181
|
+
port: config.port,
|
|
1182
|
+
host: config.host || LOCALHOST
|
|
1183
|
+
},
|
|
1189
1184
|
function (err, s) {
|
|
1190
1185
|
reqSocket = s;
|
|
1191
1186
|
if (err || socket._hasError) {
|
|
1192
1187
|
return destroy(err);
|
|
1193
1188
|
}
|
|
1189
|
+
reqSocket.on('error', destroy);
|
|
1194
1190
|
reqSocket.write(chunk);
|
|
1195
1191
|
reqSocket.pipe(socket).pipe(reqSocket);
|
|
1196
|
-
abortIfUnavailable(reqSocket);
|
|
1197
1192
|
socket.resume();
|
|
1198
1193
|
}
|
|
1199
1194
|
);
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
if (!chunk) {
|
|
1203
1198
|
//没有数据
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1199
|
+
return isWebPort ? socket.destroy() : next(chunk);
|
|
1200
|
+
}
|
|
1201
|
+
if (isHttp) {
|
|
1202
|
+
if (isEnable('forHttps') || disable.captureHttp) {
|
|
1203
|
+
next(chunk);
|
|
1204
|
+
} else {
|
|
1205
|
+
socket.resume();
|
|
1206
|
+
server.emit('connection', socket);
|
|
1207
|
+
socket.emit(
|
|
1213
1208
|
'data',
|
|
1214
1209
|
addClientInfo(socket, chunk, statusLine, clientIp, clientPort)
|
|
1215
1210
|
);
|
|
1216
|
-
|
|
1217
|
-
|
|
1211
|
+
}
|
|
1212
|
+
} else if (isEnable('forHttp') || disable.captureHttps) {
|
|
1213
|
+
return next(chunk);
|
|
1214
|
+
} else {
|
|
1215
|
+
var isHttpH2 = HTTP2_RE.test(headersStr);
|
|
1216
|
+
if (!isHttpH2 && chunk[0] != 22) {
|
|
1218
1217
|
return next(chunk);
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
}
|
|
1224
|
-
var useSNI, domain, serverKey;
|
|
1225
|
-
var handleConnect = function (port) {
|
|
1226
|
-
var promise =
|
|
1218
|
+
}
|
|
1219
|
+
var useSNI, domain, serverKey;
|
|
1220
|
+
var handleConnect = function (port) {
|
|
1221
|
+
var promise =
|
|
1227
1222
|
!isHttpH2 && !useSNI && serverAgent.existsServer(serverKey);
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1223
|
+
var httpsServer = promise && promise.cert && promise.server;
|
|
1224
|
+
if (httpsServer && httpsServer.setSecureContext) {
|
|
1225
|
+
var cert = serverAgent.createCertificate(domain);
|
|
1226
|
+
if (
|
|
1232
1227
|
cert.key !== promise.cert.key ||
|
|
1233
1228
|
cert.cert !== promise.cert.cert
|
|
1234
1229
|
) {
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
}
|
|
1230
|
+
try {
|
|
1231
|
+
cert._ctx = cert._ctx || ca.createSecureContext(cert);
|
|
1232
|
+
httpsServer.setSecureContext(cert._ctx);
|
|
1233
|
+
promise.cert = cert;
|
|
1234
|
+
} catch (e) {}
|
|
1241
1235
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1236
|
+
}
|
|
1237
|
+
util.connect(
|
|
1238
|
+
{
|
|
1239
|
+
port: port,
|
|
1240
|
+
host: LOCALHOST,
|
|
1241
|
+
localAddress: LOCALHOST
|
|
1242
|
+
},
|
|
1248
1243
|
function (err, s) {
|
|
1249
1244
|
reqSocket = s;
|
|
1250
1245
|
if (err || socket._hasError) {
|
|
1251
1246
|
return destroy(err);
|
|
1252
1247
|
}
|
|
1248
|
+
reqSocket.on('error', destroy);
|
|
1253
1249
|
socket._w2TunnelKey = reqSocket.localPort + ':' + reqSocket.remotePort;
|
|
1254
1250
|
tunnelTmplData.set(socket._w2TunnelKey, getTunnelData(socket, clientIp, clientPort, isHttpH2));
|
|
1255
1251
|
reqSocket.write(chunk);
|
|
1256
1252
|
reqSocket.pipe(socket).pipe(reqSocket);
|
|
1257
1253
|
socket.resume();
|
|
1258
|
-
abortIfUnavailable(reqSocket);
|
|
1259
1254
|
}
|
|
1260
1255
|
);
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1256
|
+
};
|
|
1257
|
+
var useNoSNIServer = function () {
|
|
1258
|
+
serverKey = requestCert ? ':' + domain : domain;
|
|
1259
|
+
serverAgent.createServer(serverKey, handlers, handleConnect, 0, 0);
|
|
1260
|
+
};
|
|
1266
1261
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
}
|
|
1275
|
-
getSNIServer(h2Handlers, handleConnect, disableH2, requestCert);
|
|
1276
|
-
} else if (isHttpH2) {
|
|
1277
|
-
getHttp2Server(h2Handlers, handleConnect);
|
|
1278
|
-
} else {
|
|
1279
|
-
useNoSNIServer();
|
|
1262
|
+
var handleRequest = function () {
|
|
1263
|
+
if (useSNI) {
|
|
1264
|
+
var disableH2 = !properties.isEnableHttp2();
|
|
1265
|
+
if (disable.http2) {
|
|
1266
|
+
disableH2 = true;
|
|
1267
|
+
} else if (enable.http2) {
|
|
1268
|
+
disableH2 = false;
|
|
1280
1269
|
}
|
|
1281
|
-
|
|
1282
|
-
if (isHttpH2) {
|
|
1283
|
-
|
|
1270
|
+
getSNIServer(h2Handlers, handleConnect, disableH2, requestCert);
|
|
1271
|
+
} else if (isHttpH2) {
|
|
1272
|
+
getHttp2Server(h2Handlers, handleConnect);
|
|
1273
|
+
} else {
|
|
1274
|
+
useNoSNIServer();
|
|
1284
1275
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1276
|
+
};
|
|
1277
|
+
if (isHttpH2) {
|
|
1278
|
+
return handleRequest();
|
|
1279
|
+
}
|
|
1280
|
+
useSNI = checkSNI(chunk);
|
|
1281
|
+
var servername = useSNI || socket.tunnelHostname;
|
|
1282
|
+
if (
|
|
1288
1283
|
!servername ||
|
|
1289
1284
|
(useSNI ? disable.captureSNI : (disable.captureNoSNI || (net.isIP(servername) && !isCaptureIp()))) ||
|
|
1290
1285
|
(socket.useProxifier && !ca.existsCustomCert(servername))
|
|
1291
1286
|
) {
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1287
|
+
return next(chunk);
|
|
1288
|
+
}
|
|
1289
|
+
var requestCert =
|
|
1295
1290
|
(enable.clientCert || enable.requestCert) &&
|
|
1296
1291
|
!disable.clientCert &&
|
|
1297
1292
|
!disable.requestCert;
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1293
|
+
domain = getDomain(servername);
|
|
1294
|
+
socket.curUrl = socket.fullUrl = 'https://' + servername;
|
|
1295
|
+
socket.useSNI = useSNI;
|
|
1296
|
+
socket.serverName = socket.servername = servername;
|
|
1297
|
+
socket.commonName = domain;
|
|
1298
|
+
loadCert(socket, function (cert) {
|
|
1299
|
+
if (socket._hasError) {
|
|
1300
|
+
return destroy();
|
|
1301
|
+
}
|
|
1302
|
+
if (cert === false) {
|
|
1303
|
+
return next(chunk);
|
|
1304
|
+
}
|
|
1305
|
+
if (cert) {
|
|
1306
|
+
domain = servername;
|
|
1307
|
+
}
|
|
1308
|
+
handleRequest();
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
},
|
|
1317
1312
|
isWebPort ? 0 : TIMEOUT
|
|
1318
1313
|
);
|
|
1319
1314
|
};
|
package/lib/index.js
CHANGED
|
@@ -11,10 +11,8 @@ var setupHttps = require('./https').setup;
|
|
|
11
11
|
var httpsUtil = require('./https/ca');
|
|
12
12
|
var rulesUtil = require('./rules/util');
|
|
13
13
|
var initDataServer = require('./util/data-server');
|
|
14
|
-
var initLogServer = require('./util/log-server');
|
|
15
14
|
var pluginMgr = require('./plugins');
|
|
16
15
|
var config = require('./config');
|
|
17
|
-
var loadService = require('./service');
|
|
18
16
|
var initSocketMgr = require('./socket-mgr');
|
|
19
17
|
var tunnelProxy = require('./tunnel');
|
|
20
18
|
var upgradeProxy = require('./upgrade');
|
|
@@ -61,7 +59,6 @@ function proxy(callback, _server) {
|
|
|
61
59
|
tunnelProxy(server, proxyEvents);
|
|
62
60
|
upgradeProxy(server);
|
|
63
61
|
initDataServer(proxyEvents);
|
|
64
|
-
initLogServer(proxyEvents);
|
|
65
62
|
rulesUtil.setup(proxyEvents);
|
|
66
63
|
var properties = rulesUtil.properties;
|
|
67
64
|
if (config.disableAllRules) {
|
|
@@ -224,7 +221,6 @@ function exportInterfaces(obj) {
|
|
|
224
221
|
obj.httpsUtil = httpsUtil;
|
|
225
222
|
obj.pluginMgr = pluginMgr;
|
|
226
223
|
obj.logger = logger;
|
|
227
|
-
obj.loadService = loadService;
|
|
228
224
|
obj.setAuth = config.setAuth;
|
|
229
225
|
obj.setUIHost = config.setUIHost;
|
|
230
226
|
obj.setPluginUIHost = config.setPluginUIHost;
|
|
@@ -254,7 +250,7 @@ function handleGlobalException(err) {
|
|
|
254
250
|
code === 'ERR_HTTP_TRAILER_INVALID' ||
|
|
255
251
|
code === 'ERR_INTERNAL_ASSERTION' ||
|
|
256
252
|
code === 'ERR_INVALID_ARG_TYPE' ||
|
|
257
|
-
(err && /finishwrite/i.test(err.message)))
|
|
253
|
+
(err && /finishwrite|'_hadError'/i.test(err.message)))
|
|
258
254
|
) {
|
|
259
255
|
return;
|
|
260
256
|
}
|