@jack2006/pangolin
Version:
pangolin is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet. As of now, it supports tcp & udp, as well as http and https protocols, where requests can be forwarded to internal services by domain name.
155 lines (136 loc) • 4.08 kB
JavaScript
const net = require('net');
const io = require('socket.io-client');
const colors = require('colors/safe');
class ClientServer {
constructor(argv) {
this.host = argv.host;
if (!this.host) {
console.log(colors.red('Error: Could not get host'));
}
this.port = argv.port || 27;
if (!this.port) {
console.log(colors.red('Error: Could not get port'));
}
if (argv.binds) {
this.binds = argv.binds;
}
this.localConnectingSockets = {};
}
host;
port;
/*
* example:
structure:
{
wall: {
local_port: 8080,
remote_port: 10004
},
www: {
local_port: 8090,
remote_port: 9080
},//本地服务名=>端口映射
}
*/
binds = {};
mainsSocket;
logger = {
info: console.log
};
init() {
this.mainsSocket = io(`http://${this.host}:${this.port}`);
// socket.on()用于接收服务端发来的消息
this.mainsSocket.on('connect', ()=>{
console.log('client connect server');
this.mainsSocket.emit('addBinds', this.binds);
for (let serverKey in this.binds) {
this.mainsSocket.on(serverKey, d => {
// console.log(`data from front server: ${d}`);
this.handleDataFromFrontServer(serverKey, d);
});
}
});
}
createLocalConnectingSocket(serverKey, requestKey, data) {
if (this.localConnectingSockets[requestKey]) {
return;
}
const that = this;
this.localConnectingSockets[requestKey] = net.connect({host: this.binds[serverKey].local_host, port: this.binds[serverKey].local_port}, () => {});
this.localConnectingSockets[requestKey].on('data', function(data) {
that.mainsSocket.emit(serverKey, { cmd: 'data', serverKey, requestKey, data });
});
this.localConnectingSockets[requestKey].on('error',err=>{
console.error(err);
that.handleWhenErrorOrClose(serverKey, requestKey);
});
this.localConnectingSockets[requestKey].on('close', () => {
that.handleWhenErrorOrClose(serverKey, requestKey);
});
}
handleWhenErrorOrClose(serverKey, requestKey) {
if (!this.localConnectingSockets[requestKey]) {
return;
}
this.localConnectingSockets[requestKey].end();
this.localConnectingSockets[requestKey] = null;
delete this.localConnectingSockets[requestKey];
this.mainsSocket.emit(serverKey, { cmd: 'close', requestKey });
}
handleDataFromFrontServer(serverKey, d) {
const { cmd, requestKey, data } = d;
if (cmd === 'create') {
this.createLocalConnectingSocket(serverKey, requestKey);
} else if (cmd === 'data') {
this.localConnectingSockets[requestKey].write(data);
} else if (cmd === 'close') {
console.log(`close requestKey: ${requestKey}`);
if (!this.localConnectingSockets[requestKey]) {
return;
}
this.localConnectingSockets[requestKey].end();
this.localConnectingSockets[requestKey] = null;
delete this.localConnectingSockets[requestKey];
}
}
/*
* binds: ex:['wall', 'www']
*/
removeBinds(binds) {
this.mainsSocket.emit('removeBinds', binds);
}
/*
{
wall: {
local_port: 8080,
remote_port: 10004
}
*/
addBinds(binds) {
const remote_ports = [];
for (let key in binds) {
remote_ports.push(binds[key].remote_port);
if (this.binds[key]) {
throw `Error: the bind of '${key}' existed`
}
}
for (let key in this.binds) {
if (remote_ports.includes(this.binds[key].remote_port)) {
throw `Error: the remote port of '${this.binds[key].remote_port}' existed`
}
}
console.log(`before, binds: ${JSON.stringify(this.binds)}`);
this.binds = {...this.binds, ...binds};
console.log(`after, binds: ${JSON.stringify(this.binds)}`);
this.mainsSocket.emit('addBinds', binds);
for (let serverKey in binds) {
this.mainsSocket.on(serverKey, d => {
this.handleDataFromFrontServer(serverKey, d);
});
}
}
start() {
this.init();
}
}
module.exports = { ClientServer };