'use strict';
|
|
const net = require('net');
|
const through = require('through2');
|
const debug = require('debug')('tcp-proxy');
|
const EventEmitter = require('events').EventEmitter;
|
|
function genThrough(interceptor) {
|
return through.obj(function(chunk, enc, done) {
|
const result = interceptor(chunk, enc);
|
const handle = data => {
|
if (data && !Buffer.isBuffer(data)) {
|
data = Buffer.from(data);
|
}
|
|
done(null, data || chunk);
|
};
|
|
if (result) {
|
if (typeof result.then === 'function') {
|
result.then(handle).catch(e => {
|
console.error(e);
|
handle();
|
});
|
} else {
|
handle(result);
|
}
|
} else {
|
handle();
|
}
|
});
|
}
|
|
module.exports = class TCPProxy extends EventEmitter {
|
constructor(options = {}) {
|
super();
|
this.host = options.host;
|
this.port = options.port;
|
this.clients = [];
|
}
|
|
createProxy({ host, port, forwardPort, forwardHost, interceptor }) {
|
const proxyHost = host || this.host;
|
const proxyPort = port || this.port;
|
forwardHost = forwardHost || '127.0.0.1';
|
interceptor = interceptor || {};
|
|
if (this.server) {
|
const args = [].slice.call(arguments);
|
return this.end().then(() => {
|
return this.createProxy.apply(this, args);
|
});
|
}
|
|
return new Promise((resolve, reject) => {
|
this.server = net
|
.createServer(client => {
|
let interceptorClient;
|
let interceptorServer;
|
debug('new connection from %o', client.address());
|
const server = net.connect({
|
port: forwardPort,
|
host: forwardHost,
|
}, () => {
|
let _client = client;
|
let _server = server;
|
|
// client interceptor
|
if (interceptor.client) {
|
interceptorClient = genThrough(interceptor.client);
|
_client = _client.pipe(interceptorClient);
|
}
|
|
// server interceptor
|
if (interceptor.server) {
|
interceptorServer = genThrough(interceptor.server);
|
_server = _server.pipe(interceptorServer);
|
}
|
|
_client.pipe(server);
|
_server.pipe(client);
|
debug(`proxy 127.0.0.1:${proxyPort} connect to ${forwardHost}:${forwardPort}`);
|
this.emit('connection', _client, _server);
|
});
|
|
const onClose = err => {
|
client.destroy();
|
server.destroy();
|
interceptorClient && interceptorClient.end();
|
interceptorServer && interceptorServer.end();
|
debug(`${forwardHost}:${forwardPort} disconnect [${err ? `error: ${err.message}` : 'close'}]`);
|
this.removeListener('close', onClose);
|
};
|
|
server.once('close', onClose);
|
server.once('error', onClose);
|
client.once('close', onClose);
|
client.once('error', onClose);
|
this.once('close', onClose);
|
})
|
.listen(proxyPort, proxyHost);
|
|
this.server.once('error', e => {
|
debug(`proxy server error: ${e.message}`);
|
reject(e);
|
});
|
|
this.server.once('listening', () => {
|
debug(`proxy server listening on ${proxyPort}`);
|
this.server.removeListener('error', reject);
|
resolve(this.server);
|
});
|
});
|
}
|
|
end() {
|
if (!this.server) {
|
return Promise.resolve();
|
}
|
|
return new Promise(resolve => {
|
this.emit('close');
|
this.server.close(() => {
|
debug('proxy server closed');
|
this.server = null;
|
resolve();
|
});
|
});
|
}
|
};
|