UNPKG

hersserver

Version:

A 'now'-alike server extension not using socket.io

241 lines (240 loc) 6.05 kB
var responding = require('./responding'); var url=require('url'); var EventEmitter = require('events').EventEmitter; var randomBytes = require('crypto').randomBytes; function logMyNow(){ return; console.log.apply(console,arguments); }; function cookieObject(cs){ var ret = {}; var rhc = cs.split(';'); if(rhc){ var rhcl=rhc.length; for(var i=0; i<rhcl; i++){ var rhce = rhc[i]; while(rhce[0]===' '){rhce=rhce.substring(1);} var ie = rhce.indexOf('='); if(ie){ ret[rhce.substring(0,ie)]=rhce.substring(ie+1); } } } return ret; }; function NowClient(id,cookie,response,diedcb){ this.clientId=id; var t = this; var oa = response.connection.remoteAddress; var op = response.connection.remotePort; this.diedcb = function(){logMyNow(id,oa,':',op,'is dead now');diedcb(t);}; this.remoteAddress = oa; this.remotePort = op; this.cookie=cookie; this.invoke('setClientId',{clientId:this.clientId},response); } NowClient.prototype.setTimeout = function(){ this.clearTimeout(); logMyNow(this.clientId,'setting Timeout'); this.timeout = setTimeout(this.diedcb,15000); }; NowClient.prototype.clearTimeout = function(){ if(this.timeout){ logMyNow(this.clientId,'clearing Timeout'); clearTimeout(this.timeout); delete this.timeout; } }; NowClient.prototype.invoke = function(method,paramobj,response){ var io = {method:method,paramobj:paramobj}; //logMyNow('should invoke',method,paramobj); if(this.response){ //logMyNow('invoking',method,'with',io); this.sendInvoke(this.response,[io]); delete this.response; if(response){ response.end(); } return; }; if(response){ this.sendInvoke(response,[io]); return; } if(!this.queue){ this.queue=[]; } this.queue.push(io); }; NowClient.prototype.hitMe = function(response){ if(this.response){ this.response.end(); delete this.response; } this.clearTimeout(); var q = this.queue; if(!q){ this.response = response; this.remoteAddress = response.connection.remoteAddress; this.remotePort = response.connection.remotePort; var t = this; //this.response.connection.setTimeout(5000); setTimeout(function(){t.invoke('',{});},5000); this.response.on('close',function(){delete t.response; t.setTimeout();}); return; } //logMyNow('dumping queue',this.queue); this.sendInvoke(response); }; NowClient.prototype.sendInvoke = function(response,arry){ var a = this.queue||[]; a = a.concat(arry||[]); if(!a.length){return;} response.write(JSON.stringify({invoke:a})); response.end(); delete this.queue; this.setTimeout(); }; function NowGroup(){ this.clients={}; } NowGroup.prototype.addUser = function(cid){ if(!cid){return;} this.clients[cid]=1; }; function Now(){ this.clients = {}; this.groups = {}; this.cnt = 0; } Now.prototype = new EventEmitter(); Now.prototype.constructor = Now; Now.prototype.byteToString = function(b){ var ret=''; ret+=(33+(b&0x0f)); b>>=4; ret+=(33+(b&0x0f)); return ret; }; Now.prototype.inc = function(){ this.cnt++; if(this.cnt>100000000){ this.cnt=1; } var rb = randomBytes(2); var rbs = ''; rbs+=this.byteToString(rb[0]); rbs+=this.byteToString(rb[1]); return (new Date()).getTime()+'-'+rbs+'-'+this.cnt; }; Now.prototype.getGroup = function(name){ var g = this.groups[name]; if(!g){ g=new NowGroup(); this.groups[name]=g; g.now = new Now(); } return g; }; Now.prototype.removeGroup = function(name){ delete this.groups[name]; }; Now.prototype.initialize = function(){ return {now:this}; }; Now.prototype.init = function(cid,request,response){ if(cid){ var m = this.clients[cid]; if(m){ m.hitMe(response); return; } logMyNow(cid,'not found'); } //logMyNow(response.socket.remoteAddress,cid,'not found'); var c = this.inc(); var t = this; var ck = {}; if(request.headers.cookie){ ck = cookieObject(request.headers.cookie); } var nc = new NowClient(c,ck,response,function(client){ t.emit('disconnect',client); t.remove(client); }); this.clients[c] = nc; this.emit('connect',nc); }; Now.prototype.deinit = function(cid){ logMyNow('deiniting',cid); var c = this.clients[cid]; if(!c){return;} this.emit('disconnect',c); this.remove(c); }; Now.prototype.remove = function(client){ var cid = client.clientId; client.clearTimeout(); for(var n in client){ delete client[n]; } for(var n in this.groups){ delete this.groups[n].clients[cid]; } delete this.clients[cid]; }; Now.prototype.broadcast = function(){ var args = Array.prototype.slice.call(arguments); for(var n in this.clients){ //logMyNow('invoking on',c,args); var c = this.clients[n]; c.invoke.apply(c,args); } }; Now.prototype.broadcastOnGroup = function(groupname){ var args = Array.prototype.slice.call(arguments,1); var g = this.groups[groupname]; if(!g){ logMyNow('group',groupname,'does not exist'); return; } var fordel=[]; logMyNow('group',groupname,'clients',g.clients); for(var n in g.clients){ logMyNow('invoking on',n,'with',args); var c = this.clients[n]; if(!c){ //logMyNow('missing client for',n); fordel.push(n); }else{ c.invoke.apply(c,args); } } var fdl = fordel.length; for(var i=0; i<fdl; i++){ delete g.clients[fordel[i]]; } }; Now.prototype.getClient = function(cid,cb){ if(typeof cb !== 'function'){return;} var c = this.clients[cid]; cb.apply(c,[c]);//provide the client as 'this' or as the first and only param }; var _NowInstance = new Now(); responding.createResponder({ name:'now/init', handler:function(cid){ _NowInstance.init(cid,this.request,this.response); }, params:['clientId'], parent:_NowInstance }); responding.createResponder({ name:'now/deinit', handler:function(cid){ _NowInstance.deinit(cid,this.request,this.response); }, params:['clientId'], parent:_NowInstance }); module.exports = _NowInstance;