| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var serversMap = module.exports._serversMap = {}; | 
					
						
							| 
									
										
										
										
											2017-05-02 17:48:58 -06:00
										 |  |  | var dgramMap = module.exports._dgramMap = {}; | 
					
						
							|  |  |  | var PromiseA = require('bluebird'); | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | module.exports.addTcpListener = function (port, handler) { | 
					
						
							|  |  |  |   return new PromiseA(function (resolve, reject) { | 
					
						
							| 
									
										
										
										
											2017-05-01 17:52:22 -06:00
										 |  |  |     var stat = serversMap[port]; | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (stat) { | 
					
						
							|  |  |  |       if (stat._closing) { | 
					
						
							| 
									
										
										
										
											2017-10-26 18:43:51 -06:00
										 |  |  |         stat.server.destroy(); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         // We're already listening on the port, so we only have 2 options. We can either
 | 
					
						
							|  |  |  |         // replace the handler or reject with an error. (Though neither is really needed
 | 
					
						
							|  |  |  |         // if the handlers are the same). Until there is reason to do otherwise we are
 | 
					
						
							|  |  |  |         // opting for the replacement.
 | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |         stat.handler = handler; | 
					
						
							|  |  |  |         resolve(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var enableDestroy = require('server-destroy'); | 
					
						
							|  |  |  |     var net = require('net'); | 
					
						
							|  |  |  |     var resolved; | 
					
						
							| 
									
										
										
										
											2017-05-29 12:50:29 -06:00
										 |  |  |     var server = net.createServer({allowHalfOpen: true}); | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     stat = serversMap[port] = { | 
					
						
							|  |  |  |       server: server | 
					
						
							|  |  |  |     , handler: handler | 
					
						
							| 
									
										
										
										
											2017-10-26 18:43:51 -06:00
										 |  |  |     , _closing: false | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 17:52:22 -06: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-26 20:16:47 -06:00
										 |  |  |     server.on('connection', function (conn) { | 
					
						
							|  |  |  |       conn.__port = port; | 
					
						
							|  |  |  |       conn.__proto = 'tcp'; | 
					
						
							|  |  |  |       stat.handler(conn); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |     server.on('close', function () { | 
					
						
							|  |  |  |       console.log('TCP server on port %d closed', port); | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |       delete serversMap[port]; | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  |     server.on('error', function (e) { | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |       if (!resolved) { | 
					
						
							|  |  |  |         reject(e); | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |       } else if (handler.onError) { | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |         handler.onError(e); | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         throw e; | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     server.listen(port, function () { | 
					
						
							|  |  |  |       resolved = true; | 
					
						
							|  |  |  |       resolve(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-10-26 18:43:51 -06:00
										 |  |  | module.exports.closeTcpListener = function (port, timeout) { | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |   return new PromiseA(function (resolve) { | 
					
						
							|  |  |  |     var stat = serversMap[port]; | 
					
						
							|  |  |  |     if (!stat) { | 
					
						
							| 
									
										
										
										
											2017-05-01 17:52:22 -06:00
										 |  |  |       resolve(); | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-26 18:43:51 -06:00
										 |  |  |     stat._closing = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var timeoutId; | 
					
						
							|  |  |  |     if (timeout) { | 
					
						
							|  |  |  |       timeoutId = setTimeout(() => stat.server.destroy(), timeout); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     stat.server.once('close', function () { | 
					
						
							|  |  |  |       clearTimeout(timeoutId); | 
					
						
							|  |  |  |       resolve(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |     stat.server.close(); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | module.exports.destroyTcpListener = function (port) { | 
					
						
							|  |  |  |   var stat = serversMap[port]; | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |   if (stat) { | 
					
						
							|  |  |  |     stat.server.destroy(); | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | module.exports.listTcpListeners = function () { | 
					
						
							| 
									
										
										
										
											2017-10-26 18:43:51 -06:00
										 |  |  |   return Object.keys(serversMap).map(Number).filter(function (port) { | 
					
						
							|  |  |  |     return port && !serversMap[port]._closing; | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-02 17:48:58 -06: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 12:14:25 -06:00
										 |  |  |     var server = dgram.createSocket({type: 'udp4', reuseAddr: true}); | 
					
						
							| 
									
										
										
										
											2017-05-02 17:48:58 -06: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-26 18:12:00 -06:00
										 |  |  | module.exports.listUdpListeners = function () { | 
					
						
							|  |  |  |   return Object.keys(dgramMap).map(Number).filter(Boolean); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-05-02 17:48:58 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  | module.exports.listeners = { | 
					
						
							|  |  |  |   tcp: { | 
					
						
							|  |  |  |     add: module.exports.addTcpListener | 
					
						
							|  |  |  |   , close: module.exports.closeTcpListener | 
					
						
							|  |  |  |   , destroy: module.exports.destroyTcpListener | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |   , list: module.exports.listTcpListeners | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-05-02 17:48:58 -06:00
										 |  |  | , udp: { | 
					
						
							|  |  |  |     add: module.exports.addUdpListener | 
					
						
							|  |  |  |   , close: module.exports.closeUdpListener | 
					
						
							| 
									
										
										
										
											2017-06-26 18:12:00 -06:00
										 |  |  |   , list: module.exports.listUdpListeners | 
					
						
							| 
									
										
										
										
											2017-05-02 17:48:58 -06:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-04-26 20:16:47 -06:00
										 |  |  | }; |