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