hersserver
Version:
A 'now'-alike server extension not using socket.io
227 lines (226 loc) • 6.69 kB
JavaScript
//magic of this module is that there needs to exist a global methodmap hash somewhere
function paramValArryFromQuery(query,paramdescarry){
if(!query){
return [];
}
if(paramdescarry==='originalhash'){
return [query];
}
var ret = [];
for(var i in paramdescarry){
var pd = paramdescarry[i];
if(typeof pd==='string'){
ret.push(query[pd]);
continue;
}
if(typeof pd!=='object'){
return;
}
val = query[pd.name];
if((pd.regex instanceof RegExp) && (!pd.regex.test(val))){
throw 'Value '+val+' for parameter '+pd+' has to satisfy RegExp '+pd.regex.source;
}
if(pd.type==='int'){
var v = parseInt(val);
if(isNaN(v)){
throw 'Value '+val+' for parameter '+pd.name+' has to be an integer';
}
val = v;
}
ret.push(val);
}
return ret;
}
function BasicResponder(){
return this;
}
BasicResponder.prototype.clientId = function clientId(){
return 0;
}
BasicResponder.prototype.stringify = function stringify(content){
this.write(JSON.stringify(content));
}
BasicResponder.prototype.writeandend = function writeandend(content){
this.write(content);
this.end();
}
BasicResponder.prototype.stringifyandend = function stringifyandend(content){
this.stringify(content);
this.end();
}
function NullResponder(){
return this;
}
NullResponder.prototype = new BasicResponder();
NullResponder.write = function write(content){}
NullResponder.end = function end(){}
function HTTPResponder(request,response,user){
if(response && (typeof response.write==='function') && (typeof response.end==='function') && response.socket && response.socket.remoteAddress){
this.request=request;
this.response = response;
this.now={socket:{handshake:{address:{address:response.socket.remoteAddress}}},user:user||{}};
}else{
this.response={write:function(){},end:function(){}};
this.now={socket:{handshake:{address:{address:''}}},user:user||{}};
}
}
HTTPResponder.prototype = new BasicResponder();
HTTPResponder.prototype.constructor = HTTPResponder;
HTTPResponder.prototype.clientId = function(){
return this.now.user.clientId;
};
HTTPResponder.prototype.write = function write(content){
this.response.write(content);
}
HTTPResponder.prototype.end = function end(){
this.response.end();
}
function HTTPRespondingContext(handler){
var handler=handler;
function realResponder(query){
if(typeof handler==='function'){
handler(new HTTPResponder(this),query);
}
}
return realResponder;
}
function NOWResponder(localnow,cb){
this.now = localnow;
if(typeof cb === 'function'){
this.cb = cb;
}else{
this.cb = function(){}
}
}
NOWResponder.prototype = new BasicResponder();
NOWResponder.prototype.constructor = NOWResponder;
NOWResponder.prototype.clientId = function clientId(){
return this.now.user.clientId;
}
NOWResponder.prototype.stringify = function stringify(content){
this.write(content);
}
NOWResponder.prototype.write = function write(content){
this.cb(content);
}
NOWResponder.prototype.end = function end(){
delete this.now;
delete this.cb;
};
function ForkedChildResponder(){
}
ForkedChildResponder.prototype = new BasicResponder();
ForkedChildResponder.prototype.clientId = function clientId(){
return 0; //process.parent.id or something alike?
};
ForkedChildResponder.prototype.stringify = function stringify(content){
this.write(content);
};
ForkedChildResponder.prototype.write = function write(content){
process.send(content);
};
ForkedChildResponder.prototype.end = function end(){};
function NOWRespondingContext(responderobj){
var _responderobj = responderobj;
function realResponder(query,cb){
if(_responderobj.level && (this.user.cookie.passcode!==serverpasscode)){
if(typeof cb==='function'){
cb();
}
return;
}
_responderobj.nowRespond(this,query,cb);
}
return realResponder;
}
function Responder(options){
methodmap[options.name]=this;
this.name = options.name;
this.handler=options.handler;
this.level=options.level;
this.params=options.params;
var nw;
if(isChildProcess){
return;
}
if(options.parent){
nw = options.parent;
console.log(options.name,'created on custom parent',nw);
}else if(options.level){
nw = now.getGroup(options.level).now;
console.log(options.name+' created on '+options.level);
}else{
nw = everyone.now;
console.log(options.name+' created on everyone');
}
nw[options.name]=NOWRespondingContext(this);
}
Responder.prototype.forkedChildRespond = function forkedChildRespond(requestobj){
this.handler.apply(new ForkedChildResponder(),paramValArryFromQuery(requestobj,this.params));
};
Responder.prototype.httpRespond = function httpRespond(request,response,query){
var l = this.level;
if(l){
var rhc = request.headers.cookie;
var passcode = (/passcode=([0-9a-zA-Z]*)/).exec(rhc);
if(!(passcode&&(passcode.length>1)&&(passcode[1]===serverpasscode))){
response.writeHead(403, {
"Content-Type" : "text/plain"
});
response.write("403 Forbidden\n");
response.end();
return;
}
}
response.writeHead(200,{'Content-Type':'text/plain'});
//response.connection.setTimeout(5);
this.handler.apply(new HTTPResponder(request,response,now.clients[query.clientId]),paramValArryFromQuery(query,this.params));
}
Responder.prototype.nowRespond = function nowRespond(localnow,query,cb){
this.handler.apply(new NOWResponder(localnow,cb),paramValArryFromQuery(query,this.params));
try{
}
catch(e){
cb({error:e.toString()});
}
}
Responder.prototype.descriptor = function descriptor(){
var ret ={name:this.name,params:[]};
for(var i in this.params){
var p = this.params[i];
if(typeof p === 'string'){
ret.params.push(p);
continue;
}
var po={};
for(var j in p){
if(j==='regex'){
po[j]=p[j].source;
}else{
po[j]=p[j];
}
}
ret.params.push(po);
}
return ret;
}
exports.createResponder = function(options){
if(typeof options !== 'object'){
console.trace('Cannot create a responder without an options object');
}
if(typeof options.name !== 'string'){
throw 'Cannot create a responder without a name';
}
if(typeof options.handler !== 'function'){
throw 'Cannot create responder '+options.name+' without a handler';
}
var r = methodmap[options.name];
if(r){
throw 'Responder for '+options.name+' already exists'+r.handler.toString();
}
r = new Responder(options);
methodmap[options.name] = r;
}
exports.isResponder = function isResponder(responderobj){
return (responderobj instanceof Responder);
}