forked from coolaj86/goldilocks.js
		
	
		
			
				
	
	
		
			178 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var serversMap = module.exports._serversMap = {};
 | |
| var dgramMap = module.exports._dgramMap = {};
 | |
| var PromiseA = require('bluebird');
 | |
| 
 | |
| module.exports.addTcpListener = function (port, handler) {
 | |
|   return new PromiseA(function (resolve, reject) {
 | |
|     var stat = serversMap[port];
 | |
| 
 | |
|     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;
 | |
|     var server = net.createServer();
 | |
| 
 | |
|     stat = serversMap[port] = {
 | |
|       server: server
 | |
|     , handler: handler
 | |
|     , _closing: null
 | |
|     };
 | |
| 
 | |
|     // 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);
 | |
| 
 | |
|     server.on('connection', function (conn) {
 | |
|       conn.__port = port;
 | |
|       conn.__proto = 'tcp';
 | |
|       stat.handler(conn);
 | |
|     });
 | |
|     server.on('error', function (e) {
 | |
|       delete serversMap[port];
 | |
| 
 | |
|       if (!resolved) {
 | |
|         reject(e);
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       if (handler.onError) {
 | |
|         handler.onError(e);
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       throw e;
 | |
|     });
 | |
| 
 | |
|     server.listen(port, function () {
 | |
|       resolved = true;
 | |
|       resolve();
 | |
|     });
 | |
|   });
 | |
| };
 | |
| module.exports.closeTcpListener = function (port) {
 | |
|   return new PromiseA(function (resolve) {
 | |
|     var stat = serversMap[port];
 | |
|     if (!stat) {
 | |
|       resolve();
 | |
|       return;
 | |
|     }
 | |
|     stat.server.on('close', function () {
 | |
|       // once the clients close too
 | |
|       delete serversMap[port];
 | |
|       if (stat._closing) {
 | |
|         stat._closing(); // resolve
 | |
|         stat._closing = null;
 | |
|       }
 | |
|       stat = null;
 | |
|     });
 | |
|     stat._closing = resolve;
 | |
|     stat.server.close();
 | |
|   });
 | |
| };
 | |
| module.exports.destroyTcpListener = function (port) {
 | |
|   var stat = serversMap[port];
 | |
|   delete serversMap[port];
 | |
|   stat.server.destroy();
 | |
|   if (stat._closing) {
 | |
|     stat._closing();
 | |
|     stat._closing = null;
 | |
|   }
 | |
|   stat = null;
 | |
| };
 | |
| 
 | |
| 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');
 | |
|     var server = dgram.createSocket('udp4');
 | |
|     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();
 | |
|   });
 | |
| };
 | |
| 
 | |
| 
 | |
| module.exports.listeners = {
 | |
|   tcp: {
 | |
|     add: module.exports.addTcpListener
 | |
|   , close: module.exports.closeTcpListener
 | |
|   , destroy: module.exports.destroyTcpListener
 | |
|   }
 | |
| , udp: {
 | |
|     add: module.exports.addUdpListener
 | |
|   , close: module.exports.closeUdpListener
 | |
|   }
 | |
| };
 |