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.

180 lines (162 loc) 5.88 kB
const net = require('net'); class ActiveServer { // natPort = 3000; // activeIO; // the main long connection socket // activeSocket; // the socket for listening local ports // localListeningSockets = {}; /* the requests from front client * {requestKey: 'UC243232345', socket: socket} */ // requests = {}; constructor(argv) { this.natPort = argv.port; if (!this.natPort) { this.natPort = 27; } this.requests = {}; this.localListeningSockets = {}; } uid(pre = '', howMany = 1, len = 4) { if (howMany > 9999) return false; const date = new Date(); let str = date.getFullYear(); str += ('0' + (date.getMonth() + 1)).slice(-2); str += ('0' + date.getDate()).slice(-2); str += ('0' + date.getHours()).slice(-2); str += ('0' + date.getMinutes()).slice(-2); str += ('0' + date.getSeconds()).slice(-2); if (!this.num || this.num.time !== str) this.num = { time: str, counter: 0 } let bd = []; for (let i = 0; i < howMany; i++) { this.num.counter++; const buffNum = (Array(len).join(0) + this.num.counter).slice(len * (-1)); bd.push(pre + str + buffNum); } return bd.length > 1 ? bd : bd[0]; } createactiveSocket(options) { if (options.port === undefined) { console.error('please set a local port for activeSocket!'); return; } this.httpServer = require('http').Server(); this.activeIO = require('socket.io')(this.httpServer); try { // 服务端监听连接状态:io的connection事件表示客户端与服务端成功建立连接,它接收一个回调函数,回调函数会接收一个socket参数。 this.activeIO.on('connection', (socket)=>{ console.log('client connect server, ok!'); this.activeSocket = socket; this.activeSocket.emit('server message', 'hello'); // socket.broadcast.emit()表示向除了自己以外的客户端发送消息 // /socket.broadcast.emit('server message', {msg:'broadcast'}); this.activeSocket.on('cli add msg', (data) => { console.log('recieve cli msg : ' + data );// hi server }); // 监听断开连接状态:socket的disconnect事件表示客户端与服务端断开连接 this.activeSocket.on('disconnect', ()=>{ console.log('connect disconnect'); for (let key in this.localListeningSockets) { this.localListeningSockets[key].close(); this.localListeningSockets[key] = null; delete this.localListeningSockets[key]; } for (let key in this.requests) { this.requests[key].socket.end(); this.requests[key] = null; delete this.requests[key]; } this.activeSocket = null; }); this.activeSocket.on('addBinds', (data) => { console.log('event addBinds') const binds = data; for (let key in binds) { if (!this.localListeningSockets[key]) { this.createLocalListeningSocket(key, {port: binds[key].remote_port}); } } }); this.activeSocket.on('removeBinds', (data) => { console.log(`remove binds: ${data}`); this.handleRemoveBinds(data); }); }); return new Promise(((resolve, reject) => { this.httpServer.listen(options.port, () => { resolve(); }); this.httpServer.on('error', e => { reject(e); }) })); } catch(ex) { console.error(ex); } } createLocalListeningSocket(serverKey, options) { if (options.port === undefined) { console.error('please set a local port for the local listening socket!'); return; } console.log(`listening the port of ${options.port}`); this.localListeningSockets[serverKey] = net.Server(async socket => { const requestKey = this.uid('UR'); console.log(`new requestKey: ${requestKey} serverKey: ${serverKey}`); console.log('ken.length: ', requestKey.length); this.requests[requestKey] = {serverKey, socket}; this.activeSocket.emit(serverKey, {cmd: 'create', requestKey}); socket.on('data',data=>{ // console.log(`data from client 1 : ${data.toString()}`); this.activeSocket.emit(serverKey, {cmd: 'data', requestKey, data}); }); socket.on('close', () => { console.log('连接关闭'); this.activeSocket.emit(serverKey, {cmd: 'close', requestKey}); delete this.requests[requestKey]; }); socket.on('error',(err)=>{socket.end();console.log(`${new Date().toLocaleString()}: ${err.message}`)}); }); this.localListeningSockets[serverKey].listen(options.port,()=>{ console.log(`NAR serv running at ${options.port}`) }); this.activeSocket.on(serverKey, d => { const { cmd, data, requestKey } = d; if (!this.requests[requestKey]) { return; } if (cmd === 'close') { this.requests[requestKey].socket.end(); this.requests[requestKey] = null; delete this.requests[requestKey]; } else if (cmd === 'data') { this.requests[requestKey].socket.write(data); } }) } /* * binds: ex:['wall', 'www'] */ handleRemoveBinds(data) { if (Object.prototype.toString.call(data) === '[object Array]') { data.forEach(key => { if (this.localListeningSockets[key]) { console.log(key); this.localListeningSockets[key].close(); this.localListeningSockets[key] = null; delete this.localListeningSockets[key]; } }); } } start() { return this.createactiveSocket({ port: this.natPort }); } close() { this.activeIO.close(); this.httpServer.close(); } } module.exports = { ActiveServer };