flocore-node/lib/services/web.js
2015-09-09 16:49:36 -04:00

228 lines
5.2 KiB
JavaScript

'use strict';
var http = require('http');
var https = require('https');
var express = require('express');
var bodyParser = require('body-parser');
var socketio = require('socket.io');
var BaseService = require('../service');
var inherits = require('util').inherits;
var index = require('../');
var log = index.log;
var fs = require('fs');
var WebService = function(options) {
var self = this;
this.node = options.node;
this.https = options.https;
this.httpsOptions = options.httpsOptions;
this.port = options.port || this.node.port || 3456;
this.node.on('ready', function() {
self.eventNames = self.getEventNames();
self.setupAllRoutes();
self.server.listen(self.port);
self.createMethodsMap();
});
};
inherits(WebService, BaseService);
WebService.dependencies = ['bitcoind'];
WebService.prototype.start = function(callback) {
var self = this;
this.app = express();
this.app.use(bodyParser.json());
// If https options are not specified, default to https if bitcoind is using https
if(this.https === undefined && this.node.services.bitcoind.configuration.rpcssl) {
this.https = true;
}
if(this.https) {
this.deriveHttpsOptions();
this.server = https.createServer(this.httpsOptions, this.app);
} else {
this.server = http.createServer(this.app);
}
this.io = socketio.listen(this.server);
this.io.on('connection', this.socketHandler.bind(this));
setImmediate(callback);
};
WebService.prototype.stop = function(callback) {
var self = this;
setImmediate(function() {
if(self.server) {
self.server.close();
}
callback();
});
};
WebService.prototype.setupAllRoutes = function() {
for(var key in this.node.services) {
var subApp = new express.Router();
var service = this.node.services[key];
if(service.getRoutePrefix && service.setupRoutes) {
this.app.use('/' + this.node.services[key].getRoutePrefix(), subApp);
this.node.services[key].setupRoutes(subApp, express);
} else {
log.debug('No routes defined for: ' + key);
}
}
};
WebService.prototype.createMethodsMap = function() {
var self = this;
var methods = this.node.getAllAPIMethods();
this.methodsMap = {};
methods.forEach(function(data) {
var name = data[0];
var instance = data[1];
var method = data[2];
var args = data[3];
self.methodsMap[name] = {
fn: function() {
return method.apply(instance, arguments);
},
args: args
};
});
};
WebService.prototype.getEventNames = function() {
var events = this.node.getAllPublishEvents();
var eventNames = [];
function addEventName(name) {
if(eventNames.indexOf(name) > -1) {
throw new Error('Duplicate event ' + name);
}
eventNames.push(name);
};
events.forEach(function(event) {
addEventName(event.name);
if(event.extraEvents) {
event.extraEvents.forEach(function(name) {
addEventName(name);
});
}
});
return eventNames;
};
WebService.prototype.socketHandler = function(socket) {
var self = this;
var bus = this.node.openBus();
socket.on('message', this.socketMessageHandler.bind(this));
socket.on('subscribe', function(name, params) {
bus.subscribe(name, params);
});
socket.on('unsubscribe', function(name, params) {
bus.unsubscribe(name, params);
});
this.eventNames.forEach(function(eventName) {
bus.on(eventName, function() {
if(socket.connected) {
var results = [];
for(var i = 0; i < arguments.length; i++) {
results.push(arguments[i]);
}
var params = [eventName].concat(results);
socket.emit.apply(socket, params);
}
});
});
socket.on('disconnect', function() {
bus.close();
});
};
WebService.prototype.socketMessageHandler = function(message, socketCallback) {
if (this.methodsMap[message.method]) {
var params = message.params;
if(!params || !params.length) {
params = [];
}
if(params.length !== this.methodsMap[message.method].args) {
return socketCallback({
error: {
message: 'Expected ' + this.methodsMap[message.method].args + ' parameter(s)'
}
});
}
var callback = function(err, result) {
var response = {};
if(err) {
response.error = {
message: err.toString()
};
}
if(result) {
response.result = result;
}
socketCallback(response);
};
params = params.concat(callback);
this.methodsMap[message.method].fn.apply(this, params);
} else {
socketCallback({
error: {
message: 'Method Not Found'
}
});
}
};
WebService.prototype.deriveHttpsOptions = function() {
var options = {};
var keyFile;
var certFile;
if(this.httpsOptions) {
keyFile = this.httpsOptions.key;
certFile = this.httpsOptions.cert;
} else {
keyFile = this.node.services.bitcoind.configuration.rpcsslprivatekeyfile;
certFile = this.node.services.bitcoind.configuration.rpcsslcertificatechainfile;
}
if(!keyFile || !certFile) {
throw new Error('Missing https options');
}
options.key = fs.readFileSync(keyFile);
options.cert = fs.readFileSync(certFile);
this.httpsOptions = options;
};
module.exports = WebService;