From 5677964651fdb32777365b03aecca2a65c8da480 Mon Sep 17 00:00:00 2001 From: Patrick Nagurny Date: Wed, 9 Sep 2015 16:39:21 -0400 Subject: [PATCH] add https to web service --- lib/services/web.js | 42 ++++++++++++++- test/services/web.unit.js | 105 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 142 insertions(+), 5 deletions(-) diff --git a/lib/services/web.js b/lib/services/web.js index b5b2bca5..af74a1c7 100644 --- a/lib/services/web.js +++ b/lib/services/web.js @@ -1,6 +1,7 @@ 'use strict'; var http = require('http'); +var https = require('https'); var express = require('express'); var bodyParser = require('body-parser'); var socketio = require('socket.io'); @@ -8,10 +9,13 @@ 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() { @@ -24,14 +28,24 @@ var WebService = function(options) { inherits(WebService, BaseService); -WebService.dependencies = []; +WebService.dependencies = ['bitcoind']; WebService.prototype.start = function(callback) { var self = this; this.app = express(); this.app.use(bodyParser.json()); - this.server = http.createServer(this.app); + // 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)); @@ -186,4 +200,28 @@ WebService.prototype.socketMessageHandler = function(message, socketCallback) { } }; +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; diff --git a/test/services/web.unit.js b/test/services/web.unit.js index b0c34eaf..4987ea1f 100644 --- a/test/services/web.unit.js +++ b/test/services/web.unit.js @@ -2,17 +2,65 @@ var should = require('chai').should(); var sinon = require('sinon'); -var WebService = require('../../lib/services/web'); var EventEmitter = require('events').EventEmitter; +var proxyquire = require('proxyquire'); + +var httpStub = { + createServer: sinon.spy() +}; +var httpsStub = { + createServer: sinon.spy() +}; +var fsStub = { + readFileSync: function(arg1) { + return arg1 + '-buffer'; + } +}; + +var WebService = proxyquire('../../lib/services/web', {http: httpStub, https: httpsStub, fs: fsStub}); describe('WebService', function() { var defaultNode = new EventEmitter(); describe('#start', function() { - it('should call the callback with no error', function(done) { - var web = new WebService({node: defaultNode}); + beforeEach(function() { + httpStub.createServer.reset(); + httpsStub.createServer.reset(); + }); + it('should create an http server if no options are specified and bitcoind is not configured for https', function(done) { + var node = new EventEmitter(); + node.services = { + bitcoind: { + configuration: { + rpcssl: 0 + } + } + }; + + var web = new WebService({node: node}); + web.deriveHttpsOptions = sinon.spy(); web.start(function(err) { should.not.exist(err); + httpStub.createServer.called.should.equal(true); + done(); + }); + }); + + it('should create an https server if no options are specified and bitcoind is configured for https', function(done) { + var node = new EventEmitter(); + node.services = { + bitcoind: { + configuration: { + rpcssl: 1 + } + } + }; + + var web = new WebService({node: node}); + web.deriveHttpsOptions = sinon.spy(); + web.start(function(err) { + should.not.exist(err); + httpsStub.createServer.called.should.equal(true); done(); }); }); @@ -291,4 +339,55 @@ describe('WebService', function() { }); }); + describe('#deriveHttpsOptions', function() { + it('should use the httpsOptions from the config if specified', function() { + var web = new WebService({ + node: defaultNode, + https: true, + httpsOptions: { + key: 'key', + cert: 'cert' + } + }); + + web.deriveHttpsOptions(); + web.httpsOptions.key.should.equal('key-buffer'); + web.httpsOptions.cert.should.equal('cert-buffer'); + }); + + it('should use bitcoind\'s https options if there is no config', function() { + var node = new EventEmitter(); + node.services = { + bitcoind: { + configuration: { + rpcssl: 1, + rpcsslprivatekeyfile: 'rpckey', + rpcsslcertificatechainfile: 'rpccert' + } + } + }; + var web = new WebService({ + node: node + }); + + web.deriveHttpsOptions(); + web.httpsOptions.key.should.equal('rpckey-buffer'); + web.httpsOptions.cert.should.equal('rpccert-buffer'); + }); + + it('should throw an error if https is specified but key or cert is not specified', function() { + var web = new WebService({ + node: defaultNode, + https: true, + httpsOptions: { + key: 'key' + } + }); + + (function() { + web.deriveHttpsOptions(); + }).should.throw('Missing https options'); + }); + }); + }); \ No newline at end of file