2017-04-27 02:16:47 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var serversMap = module.exports._serversMap = {};
|
2017-05-02 23:48:58 +00:00
|
|
|
var dgramMap = module.exports._dgramMap = {};
|
|
|
|
var PromiseA = require('bluebird');
|
2017-04-27 02:16:47 +00:00
|
|
|
|
|
|
|
module.exports.addTcpListener = function (port, handler) {
|
|
|
|
return new PromiseA(function (resolve, reject) {
|
2017-05-01 23:52:22 +00:00
|
|
|
var stat = serversMap[port];
|
2017-04-27 02:16:47 +00:00
|
|
|
|
|
|
|
if (stat) {
|
|
|
|
if (stat._closing) {
|
|
|
|
module.exports.destroyTcpListener(port);
|
|
|
|
}
|
|
|
|
else if (handler !== stat.handler) {
|
|
|
|
|
|
|
|
// we'll replace the current listener
|
|
|
|
stat.handler = handler;
|
|
|
|
resolve();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// this exact listener is already open
|
|
|
|
resolve();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var enableDestroy = require('server-destroy');
|
|
|
|
var net = require('net');
|
|
|
|
var resolved;
|
2017-05-29 18:50:29 +00:00
|
|
|
var server = net.createServer({allowHalfOpen: true});
|
2017-04-27 02:16:47 +00:00
|
|
|
|
|
|
|
stat = serversMap[port] = {
|
|
|
|
server: server
|
|
|
|
, handler: handler
|
2017-05-02 23:48:58 +00:00
|
|
|
, _closing: null
|
2017-04-27 02:16:47 +00:00
|
|
|
};
|
|
|
|
|
2017-05-01 23:52:22 +00:00
|
|
|
// Add .destroy so we can close all open connections. Better if added before listen
|
|
|
|
// to eliminate any possibility of it missing an early connection in it's records.
|
|
|
|
enableDestroy(server);
|
|
|
|
|
2017-04-27 02:16:47 +00:00
|
|
|
server.on('connection', function (conn) {
|
|
|
|
conn.__port = port;
|
|
|
|
conn.__proto = 'tcp';
|
|
|
|
stat.handler(conn);
|
|
|
|
});
|
2017-06-27 00:12:00 +00:00
|
|
|
server.on('close', function () {
|
|
|
|
console.log('TCP server on port %d closed', port);
|
2017-04-27 02:16:47 +00:00
|
|
|
delete serversMap[port];
|
2017-06-27 00:12:00 +00:00
|
|
|
});
|
|
|
|
server.on('error', function (e) {
|
2017-04-27 02:16:47 +00:00
|
|
|
if (!resolved) {
|
|
|
|
reject(e);
|
2017-06-27 00:12:00 +00:00
|
|
|
} else if (handler.onError) {
|
2017-04-27 02:16:47 +00:00
|
|
|
handler.onError(e);
|
2017-06-27 00:12:00 +00:00
|
|
|
} else {
|
|
|
|
throw e;
|
2017-04-27 02:16:47 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
server.listen(port, function () {
|
|
|
|
resolved = true;
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
module.exports.closeTcpListener = function (port) {
|
|
|
|
return new PromiseA(function (resolve) {
|
|
|
|
var stat = serversMap[port];
|
|
|
|
if (!stat) {
|
2017-05-01 23:52:22 +00:00
|
|
|
resolve();
|
2017-04-27 02:16:47 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-06-27 00:12:00 +00:00
|
|
|
stat.server.once('close', resolve);
|
2017-04-27 02:16:47 +00:00
|
|
|
stat.server.close();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
module.exports.destroyTcpListener = function (port) {
|
|
|
|
var stat = serversMap[port];
|
2017-06-27 00:12:00 +00:00
|
|
|
if (stat) {
|
|
|
|
stat.server.destroy();
|
2017-04-27 02:16:47 +00:00
|
|
|
}
|
2017-06-27 00:12:00 +00:00
|
|
|
};
|
|
|
|
module.exports.listTcpListeners = function () {
|
|
|
|
return Object.keys(serversMap).map(Number).filter(Boolean);
|
2017-04-27 02:16:47 +00:00
|
|
|
};
|
|
|
|
|
2017-06-27 00:12:00 +00:00
|
|
|
|
2017-05-02 23:48:58 +00:00
|
|
|
module.exports.addUdpListener = function (port, handler) {
|
|
|
|
return new PromiseA(function (resolve, reject) {
|
|
|
|
var stat = dgramMap[port];
|
|
|
|
|
|
|
|
if (stat) {
|
|
|
|
// we'll replace the current listener
|
|
|
|
stat.handler = handler;
|
|
|
|
resolve();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var dgram = require('dgram');
|
2017-06-09 18:14:25 +00:00
|
|
|
var server = dgram.createSocket({type: 'udp4', reuseAddr: true});
|
2017-05-02 23:48:58 +00:00
|
|
|
var resolved = false;
|
|
|
|
|
|
|
|
stat = dgramMap[port] = {
|
|
|
|
server: server
|
|
|
|
, handler: handler
|
|
|
|
};
|
|
|
|
|
|
|
|
server.on('message', function (msg, rinfo) {
|
|
|
|
msg._size = rinfo.size;
|
|
|
|
msg._remoteFamily = rinfo.family;
|
|
|
|
msg._remoteAddress = rinfo.address;
|
|
|
|
msg._remotePort = rinfo.port;
|
|
|
|
msg._port = port;
|
|
|
|
stat.handler(msg);
|
|
|
|
});
|
|
|
|
|
|
|
|
server.on('error', function (err) {
|
|
|
|
if (!resolved) {
|
|
|
|
delete dgramMap[port];
|
|
|
|
reject(err);
|
|
|
|
}
|
|
|
|
else if (stat.handler.onError) {
|
|
|
|
stat.handler.onError(err);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
server.on('close', function () {
|
|
|
|
delete dgramMap[port];
|
|
|
|
});
|
|
|
|
|
|
|
|
server.bind(port, function () {
|
|
|
|
resolved = true;
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
module.exports.closeUdpListener = function (port) {
|
|
|
|
var stat = dgramMap[port];
|
|
|
|
if (!stat) {
|
|
|
|
return PromiseA.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
return new PromiseA(function (resolve) {
|
|
|
|
stat.server.once('close', resolve);
|
|
|
|
stat.server.close();
|
|
|
|
});
|
|
|
|
};
|
2017-06-27 00:12:00 +00:00
|
|
|
module.exports.listUdpListeners = function () {
|
|
|
|
return Object.keys(dgramMap).map(Number).filter(Boolean);
|
|
|
|
};
|
2017-05-02 23:48:58 +00:00
|
|
|
|
|
|
|
|
2017-04-27 02:16:47 +00:00
|
|
|
module.exports.listeners = {
|
|
|
|
tcp: {
|
|
|
|
add: module.exports.addTcpListener
|
|
|
|
, close: module.exports.closeTcpListener
|
|
|
|
, destroy: module.exports.destroyTcpListener
|
2017-06-27 00:12:00 +00:00
|
|
|
, list: module.exports.listTcpListeners
|
2017-04-27 02:16:47 +00:00
|
|
|
}
|
2017-05-02 23:48:58 +00:00
|
|
|
, udp: {
|
|
|
|
add: module.exports.addUdpListener
|
|
|
|
, close: module.exports.closeUdpListener
|
2017-06-27 00:12:00 +00:00
|
|
|
, list: module.exports.listUdpListeners
|
2017-05-02 23:48:58 +00:00
|
|
|
}
|
2017-04-27 02:16:47 +00:00
|
|
|
};
|