UNPKG

@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
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 };