mirror of
https://github.com/alibaba/anyproxy.git
synced 2025-04-24 08:41:31 +00:00
fix https proxy server for ip host
This commit is contained in:
parent
2516ea2d57
commit
4fb20d28ce
@ -2,6 +2,7 @@
|
|||||||
// https://jestjs.io/docs/en/configuration.html
|
// https://jestjs.io/docs/en/configuration.html
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
testTimeout: 10 * 1000,
|
||||||
// All imported modules in your tests should be mocked automatically
|
// All imported modules in your tests should be mocked automatically
|
||||||
// automock: false,
|
// automock: false,
|
||||||
|
|
||||||
|
@ -4,19 +4,28 @@
|
|||||||
const async = require('async'),
|
const async = require('async'),
|
||||||
https = require('https'),
|
https = require('https'),
|
||||||
tls = require('tls'),
|
tls = require('tls'),
|
||||||
|
assert = require('assert'),
|
||||||
crypto = require('crypto'),
|
crypto = require('crypto'),
|
||||||
color = require('colorful'),
|
color = require('colorful'),
|
||||||
certMgr = require('./certMgr'),
|
certMgr = require('./certMgr'),
|
||||||
logUtil = require('./log'),
|
logUtil = require('./log'),
|
||||||
util = require('./util'),
|
util = require('./util'),
|
||||||
wsServerMgr = require('./wsServerMgr'),
|
wsServerMgr = require('./wsServerMgr'),
|
||||||
co = require('co'),
|
|
||||||
assert = require('assert'),
|
|
||||||
constants = require('constants'),
|
constants = require('constants'),
|
||||||
asyncTask = require('async-task-mgr');
|
asyncTask = require('async-task-mgr');
|
||||||
|
|
||||||
const createSecureContext = tls.createSecureContext || crypto.createSecureContext;
|
/**
|
||||||
function SNIPrepareCert(serverName, SNICallback) {
|
* Create an https server
|
||||||
|
*
|
||||||
|
* @param {object} config
|
||||||
|
* @param {number} config.port
|
||||||
|
* @param {function} config.handler
|
||||||
|
*/
|
||||||
|
function createHttpsSNIServer(port, handler) {
|
||||||
|
assert(port && handler, 'invalid param for https SNI server');
|
||||||
|
|
||||||
|
const createSecureContext = tls.createSecureContext || crypto.createSecureContext;
|
||||||
|
function SNIPrepareCert(serverName, SNICallback) {
|
||||||
let keyContent,
|
let keyContent,
|
||||||
crtContent,
|
crtContent,
|
||||||
ctx;
|
ctx;
|
||||||
@ -52,69 +61,67 @@ function SNIPrepareCert(serverName, SNICallback) {
|
|||||||
} else {
|
} else {
|
||||||
logUtil.printLog('err occurred when prepare certs for SNI - ' + err, logUtil.T_ERR);
|
logUtil.printLog('err occurred when prepare certs for SNI - ' + err, logUtil.T_ERR);
|
||||||
logUtil.printLog('err occurred when prepare certs for SNI - ' + err.stack, logUtil.T_ERR);
|
logUtil.printLog('err occurred when prepare certs for SNI - ' + err.stack, logUtil.T_ERR);
|
||||||
|
SNICallback(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
//config.port - port to start https server
|
|
||||||
//config.handler - request handler
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an https server
|
|
||||||
*
|
|
||||||
* @param {object} config
|
|
||||||
* @param {number} config.port
|
|
||||||
* @param {function} config.handler
|
|
||||||
*/
|
|
||||||
function createHttpsServer(config) {
|
|
||||||
if (!config || !config.port || !config.handler) {
|
|
||||||
throw (new Error('please assign a port'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const server = https.createServer({
|
const server = https.createServer({
|
||||||
secureOptions: constants.SSL_OP_NO_SSLv3 || constants.SSL_OP_NO_TLSv1,
|
secureOptions: constants.SSL_OP_NO_SSLv3 || constants.SSL_OP_NO_TLSv1,
|
||||||
SNICallback: SNIPrepareCert,
|
SNICallback: SNIPrepareCert,
|
||||||
}, config.handler).listen(config.port);
|
}, handler).listen(port);
|
||||||
resolve(server);
|
resolve(server);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function createHttpsIPServer(ip, port, handler) {
|
||||||
*
|
assert(ip && port && handler, 'invalid param for https IP server');
|
||||||
*
|
|
||||||
* @class httpsServerMgr
|
return new Promise((resolve, reject) => {
|
||||||
* @param {object} config
|
certMgr.getCertificate(ip, (err, keyContent, crtContent) => {
|
||||||
* @param {function} config.handler handler to deal https request
|
if (err) return reject(err);
|
||||||
*
|
const server = https.createServer({
|
||||||
*/
|
secureOptions: constants.SSL_OP_NO_SSLv3 || constants.SSL_OP_NO_TLSv1,
|
||||||
|
key: keyContent,
|
||||||
|
cert: crtContent,
|
||||||
|
}, handler).listen(port);
|
||||||
|
|
||||||
|
resolve(server);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class httpsServerMgr {
|
class httpsServerMgr {
|
||||||
constructor(config) {
|
constructor(config) {
|
||||||
assert(config, 'config is required');
|
if (!config || !config.handler) {
|
||||||
assert(config.handler && config.wsHandler, 'handler and wsHandler are required');
|
throw new Error('handler is required');
|
||||||
assert(config.hostname, 'hostname is required');
|
}
|
||||||
this.hostname = config.hostname;
|
|
||||||
this.handler = config.handler;
|
|
||||||
this.wsHandler = config.wsHandler;
|
|
||||||
this.httpsAsyncTask = new asyncTask();
|
this.httpsAsyncTask = new asyncTask();
|
||||||
this.asyncTaskName = `https_${Math.random()}`;
|
this.handler = config.handler;
|
||||||
this.httpsServer = null;
|
this.wsHandler = config.wsHandler
|
||||||
|
this.asyncSNITaskName = `https_SNI_${Math.random()}`;
|
||||||
|
this.activeServers = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getSharedHttpsServer() {
|
getSharedHttpsServer(hostname) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const finalHost = self.hostname;
|
const ifIPHost = hostname && util.isIp(hostname);
|
||||||
function prepareServer(callback) {
|
const serverHost = '127.0.0.1';
|
||||||
let instancePort;
|
|
||||||
co(util.getFreePort)
|
|
||||||
.then(co.wrap(function *(port) {
|
|
||||||
instancePort = port;
|
|
||||||
let httpsServer = null;
|
|
||||||
|
|
||||||
httpsServer = yield createHttpsServer({
|
function prepareServer(callback) {
|
||||||
port,
|
let port;
|
||||||
handler: self.handler
|
Promise.resolve(util.getFreePort())
|
||||||
});
|
.then(freePort => {
|
||||||
|
port = freePort;
|
||||||
|
if (ifIPHost) {
|
||||||
|
return createHttpsIPServer(hostname, port, self.handler);
|
||||||
|
} else {
|
||||||
|
return createHttpsSNIServer(port, self.handler);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(httpsServer => {
|
||||||
|
self.activeServers.push(httpsServer);
|
||||||
|
|
||||||
wsServerMgr.getWsServer({
|
wsServerMgr.getWsServer({
|
||||||
server: httpsServer,
|
server: httpsServer,
|
||||||
@ -125,22 +132,20 @@ class httpsServerMgr {
|
|||||||
logUtil.debug('will let WebSocket server to handle the upgrade event');
|
logUtil.debug('will let WebSocket server to handle the upgrade event');
|
||||||
});
|
});
|
||||||
|
|
||||||
self.httpsServer = httpsServer;
|
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
host: finalHost,
|
host: serverHost,
|
||||||
port: instancePort,
|
port,
|
||||||
};
|
};
|
||||||
callback(null, result);
|
callback(null, result);
|
||||||
return result;
|
})
|
||||||
}))
|
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
callback(e);
|
callback(e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// same server for same host
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
self.httpsAsyncTask.addTask(self.asyncTaskName, prepareServer, (error, serverInfo) => {
|
self.httpsAsyncTask.addTask(ifIPHost ? hostname : serverHost, prepareServer, (error, serverInfo) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
@ -151,7 +156,9 @@ class httpsServerMgr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
return this.httpsServer && this.httpsServer.close();
|
this.activeServers.forEach(server => {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ module.exports.getByteSize = function (content) {
|
|||||||
/*
|
/*
|
||||||
* identify whether the
|
* identify whether the
|
||||||
*/
|
*/
|
||||||
module.exports.isIpDomain = function (domain) {
|
module.exports.isIp = function (domain) {
|
||||||
if (!domain) {
|
if (!domain) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,67 @@
|
|||||||
|
const tls = require('tls');
|
||||||
const httpsServerMgr = require('../../lib/httpsServerMgr');
|
const httpsServerMgr = require('../../lib/httpsServerMgr');
|
||||||
|
|
||||||
describe('httpsServerMgr', () => {
|
describe('httpsServerMgr', () => {
|
||||||
it('get https server', async () => {
|
let serverMgrInstance;
|
||||||
const serverMgr = new httpsServerMgr({
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
serverMgrInstance = new httpsServerMgr({
|
||||||
hostname: '127.0.0.1',
|
hostname: '127.0.0.1',
|
||||||
handler: () => {
|
handler: (req, res) => {
|
||||||
console.log('this is handler');
|
res.end('hello world');
|
||||||
},
|
|
||||||
wsHandler: () => {
|
|
||||||
console.log('this is handler');
|
|
||||||
},
|
},
|
||||||
|
wsHandler: () => { },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await serverMgrInstance.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('SNI server should work properly', async () => {
|
||||||
|
const sniServerA = await serverMgrInstance.getSharedHttpsServer('a.anyproxy.io');
|
||||||
|
const sniServerB = await serverMgrInstance.getSharedHttpsServer('b.anyproxy.io');
|
||||||
|
|
||||||
|
expect(sniServerA).toEqual(sniServerB); // SNI - common server
|
||||||
|
|
||||||
|
const connectHostname = 'some_new_host.anyproxy.io';
|
||||||
|
const connectOpt = {
|
||||||
|
servername: connectHostname, // servername is required for sni server
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
}
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const socketToSNIServer = tls.connect(sniServerA.port, '127.0.0.1', connectOpt, (tlsSocket) => {
|
||||||
|
// console.log('client to SNI server connected, ', socketToSNIServer.authorized ? 'authorized' : 'unauthorized');
|
||||||
|
const certSubject = socketToSNIServer.getPeerCertificate().subject;
|
||||||
|
expect(certSubject.CN).toEqual(connectHostname);
|
||||||
|
socketToSNIServer.end();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
socketToSNIServer.on('keylog', line => {
|
||||||
|
console.log(line);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('IP server should work properly', async () => {
|
||||||
|
const ipServerHost = '1.2.3.4';
|
||||||
|
const anotherSNIServer = await serverMgrInstance.getSharedHttpsServer('c.anyproxy.io');
|
||||||
|
const ipServerA = await serverMgrInstance.getSharedHttpsServer(ipServerHost);
|
||||||
|
const ipServerB = await serverMgrInstance.getSharedHttpsServer('5.6.7.8');
|
||||||
|
expect(ipServerA).not.toEqual(ipServerB);
|
||||||
|
expect(anotherSNIServer).not.toEqual(ipServerA);
|
||||||
|
|
||||||
|
const connectOpt = {
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
}
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const socketToIpServer = tls.connect(ipServerA.port, '127.0.0.1', connectOpt, () => {
|
||||||
|
const certSubject = socketToIpServer.getPeerCertificate().subject;
|
||||||
|
expect(certSubject.CN).toEqual(ipServerHost);
|
||||||
|
socketToIpServer.end();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
await serverMgr.getSharedHttpsServer();
|
|
||||||
serverMgr.close();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user