var url = require('url');
var utils = require('./utils.js');
var fs = require('fs');
var MemoryMessageStore = require("./memoryMessageStore.js").MemoryMessageStore;
if(typeof Class === 'undefined') Class = require('./Class.js').Class;
var Backplane = Class.extend({
/**
* Handler for backplane server
* @class Backplane
* @param {object} parameters Parameters to pass to the server. Specify
* <ul>
* <li>parameters.secrets A hash of bus:key. Buses will be created automatically. Secrets are used for POST authentication.</li>
*/
parameters : {
enumerable:true
}
,init : function(){
this.messageStore = new MemoryMessageStore();
var entry;
// Spawn buses for each entry
for (var i in this.parameters.secrets){
entry = this.parameters.secrets[i];
this.messageStore.addBus(entry.bus)
}
}
,authHandler: function(bus,key){
/**
* Read the password, and compare it with the one in the secrets.json file
* @method authHandler
* @param {string} bus bus to interact with
* @param {string} key secret key for this bus
*/
var res;
res = false;
var entry;
for (var i in this.parameters.secrets){
entry = this.parameters.secrets[i];
if (entry.bus == bus && entry.key == key){
res = true
}
}
return res;
}
,decode64Handler: function(encodedString){
throw { name: "Backplane: Option not set exception", message: "Backplane needs a base64 decoder to function." }
}
,validate: function(request){
//Check it is the Basic HTTP Authentication otherwise throw an exception
var basicAuthRegex = /^Basic (.*)/;
var result = basicAuthRegex.exec(request.headers.authorization);
if(!result) {
throw {
name: "AuthenticationError"
,message: "This server only supports Basic authentication."
,authenticationHeader: request.headers.authorization
}
} else {
//Check the result with the authentication handler
var authStr = result[1];
authStr = this.decode64Handler(authStr).split(':');
return this.authHandler(authStr[0],authStr[1]);
}
}
,processGetBus : function(res,options){
if(options.callback){
return function(messageArray){
res.writeHead(200, {"Content-Type": "text/javascript"});
res.end(options.callback+"("+JSON.stringify(messageArray)+")");
};
} else {
return function(messageArray){
res.writeHead(200, {"Content-Type": "application/json"});
res.end(JSON.stringify(messageArray));
};
}
}
,processGetChannel : function(res,options){
if(options.callback){
return function(messageArray){
res.writeHead(200, {"Content-Type": "text/javascript"});
res.end(options.callback+"("+JSON.stringify(messageArray)+")");
};
} else {
return function(messageArray){
res.writeHead(200, {"Content-Type": "application/json"});
res.end(JSON.stringify(messageArray));
};
}
}
,processPost : function(req){
req.content = "";
return function(chunk){
req.content += chunk;
}
}
,postEnd : function(request,response,bus,channel){
var scope = this;
return function(){
try{
scope.messageStore.save(bus,channel,request.content);
response.writeHead(200, {"Content-Type": "text/plain"});
response.end();
} catch (e){
// Bad request
response.writeHead(400, {"Content-Type":"text/plain"});
response.end(JSON.stringify({error:{type:e.name,message:e.message,code:"400"}}));
}
}
}
,handler : function(options){
options = options || {};
if(!options.decode64Handler) options.decode64Handler = require('base64').decode;
utils.mergeOptions(this,['authHandler','decode64Handler','messageStore'],options);
var scope = this;
return function(req,res){
var options = {};
var param = url.parse(req.url,true).query;
var paths = url.parse(req.url,true).pathname.split('/');
var bus = paths[3], channel = paths[5];
if(param.callback){
options.callback = param.callback;
}
if (param.since){
options.since = param.since;
}
switch(req.method){
case "GET":
try {
if(channel) scope.messageStore.getChannelMessages(bus,channel,options,scope.processGetChannel(res,options));
else scope.messageStore.getBusMessages(bus,options,scope.processGetBus(res,options));
} catch (e) {
// Something went wrong.
res.writeHead(400, {"Content-Type":"text/plain"});
res.end(JSON.stringify({error:{type:e.name,message:e.message,code:"400"}}));
}
break;
case "POST":
try{
if(scope.validate(req)){
req.addListener('data',scope.processPost(req));
req.addListener('end',scope.postEnd(req,res,bus,channel));
}
else{
// Invalid login/password
res.writeHead(401, {"Content-Type": "text/plain"});
res.end(JSON.stringify({error:{type:"Unauthorized",message:"Wrong username and/or password.", code:"401"}}));
}
} catch(e){
// Something went wrong.
res.writeHead(400, {"Content-Type":"text/plain"});
res.end(JSON.stringify({error:{type:e.name,message:e.message,code:"400"}}));
}
break;
default:
throw "Method Not implemented";
}
};
}
,connectHandler : function(options){
var callback = this.handler(options);
return function(req,res,next) {
var urlRegex = /^\/v1\/bus\//;
if(urlRegex.test(req.url)){
callback(req,res);
}
else next();
}
}
});
exports.Backplane = Backplane;