Merge pull request #213 from pnagurny/feature/https
Add https to web service
This commit is contained in:
commit
c5dfc26b28
|
@ -32,6 +32,8 @@ function Node(config) {
|
||||||
$.checkState(config.datadir, 'Node config expects "datadir"');
|
$.checkState(config.datadir, 'Node config expects "datadir"');
|
||||||
this.datadir = config.datadir;
|
this.datadir = config.datadir;
|
||||||
this.port = config.port;
|
this.port = config.port;
|
||||||
|
this.https = config.https;
|
||||||
|
this.httpsOptions = config.httpsOptions;
|
||||||
|
|
||||||
this._setNetwork(config);
|
this._setNetwork(config);
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,6 @@ var BASE_PACKAGE = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var BASE_BITCOIN_CONFIG = 'whitelist=127.0.0.1\n' + 'txindex=1\n';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will create a directory and bitcoin.conf file for Bitcoin.
|
* Will create a directory and bitcoin.conf file for Bitcoin.
|
||||||
* @param {String} dataDir - The absolute path
|
* @param {String} dataDir - The absolute path
|
||||||
|
@ -38,12 +36,9 @@ function createBitcoinDirectory(datadir, done) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
fs.writeFileSync(datadir + '/bitcoin.conf', BASE_BITCOIN_CONFIG);
|
|
||||||
} catch(e) {
|
|
||||||
done(e);
|
|
||||||
}
|
|
||||||
done();
|
done();
|
||||||
|
|
||||||
|
// Don't create the configuration yet
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,13 @@ Bitcoin.prototype._loadConfiguration = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(configPath)) {
|
if (!fs.existsSync(configPath)) {
|
||||||
fs.writeFileSync(configPath, Bitcoin.DEFAULT_CONFIG);
|
var defaultConfig = Bitcoin.DEFAULT_CONFIG;
|
||||||
|
if(this.node.https && this.node.httpsOptions) {
|
||||||
|
defaultConfig += 'rpcssl=1\n';
|
||||||
|
defaultConfig += 'rpcsslprivatekeyfile=' + this.node.httpsOptions.key + '\n';
|
||||||
|
defaultConfig += 'rpcsslcertificatechainfile=' + this.node.httpsOptions.cert + '\n';
|
||||||
|
}
|
||||||
|
fs.writeFileSync(configPath, defaultConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
var file = fs.readFileSync(configPath);
|
var file = fs.readFileSync(configPath);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var http = require('http');
|
var http = require('http');
|
||||||
|
var https = require('https');
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var bodyParser = require('body-parser');
|
var bodyParser = require('body-parser');
|
||||||
var socketio = require('socket.io');
|
var socketio = require('socket.io');
|
||||||
|
@ -8,10 +9,13 @@ var BaseService = require('../service');
|
||||||
var inherits = require('util').inherits;
|
var inherits = require('util').inherits;
|
||||||
var index = require('../');
|
var index = require('../');
|
||||||
var log = index.log;
|
var log = index.log;
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
var WebService = function(options) {
|
var WebService = function(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.node = options.node;
|
this.node = options.node;
|
||||||
|
this.https = options.https || this.node.https;
|
||||||
|
this.httpsOptions = options.httpsOptions || this.node.httpsOptions;
|
||||||
this.port = options.port || this.node.port || 3456;
|
this.port = options.port || this.node.port || 3456;
|
||||||
|
|
||||||
this.node.on('ready', function() {
|
this.node.on('ready', function() {
|
||||||
|
@ -31,7 +35,12 @@ WebService.prototype.start = function(callback) {
|
||||||
this.app = express();
|
this.app = express();
|
||||||
this.app.use(bodyParser.json());
|
this.app.use(bodyParser.json());
|
||||||
|
|
||||||
|
if(this.https) {
|
||||||
|
this.transformHttpsOptions();
|
||||||
|
this.server = https.createServer(this.httpsOptions, this.app);
|
||||||
|
} else {
|
||||||
this.server = http.createServer(this.app);
|
this.server = http.createServer(this.app);
|
||||||
|
}
|
||||||
|
|
||||||
this.io = socketio.listen(this.server);
|
this.io = socketio.listen(this.server);
|
||||||
this.io.on('connection', this.socketHandler.bind(this));
|
this.io.on('connection', this.socketHandler.bind(this));
|
||||||
|
@ -186,4 +195,15 @@ WebService.prototype.socketMessageHandler = function(message, socketCallback) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
WebService.prototype.transformHttpsOptions = function() {
|
||||||
|
if(!this.httpsOptions || !this.httpsOptions.key || !this.httpsOptions.cert) {
|
||||||
|
throw new Error('Missing https options');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.httpsOptions = {
|
||||||
|
key: fs.readFileSync(this.httpsOptions.key),
|
||||||
|
cert: fs.readFileSync(this.httpsOptions.cert)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = WebService;
|
module.exports = WebService;
|
||||||
|
|
|
@ -70,11 +70,9 @@ describe('#create', function() {
|
||||||
|
|
||||||
var configPath = testDir + '/mynode/bitcore-node.json';
|
var configPath = testDir + '/mynode/bitcore-node.json';
|
||||||
var packagePath = testDir + '/mynode/package.json';
|
var packagePath = testDir + '/mynode/package.json';
|
||||||
var bitcoinConfig = testDir + '/mynode/data/bitcoin.conf';
|
|
||||||
|
|
||||||
should.equal(fs.existsSync(configPath), true);
|
should.equal(fs.existsSync(configPath), true);
|
||||||
should.equal(fs.existsSync(packagePath), true);
|
should.equal(fs.existsSync(packagePath), true);
|
||||||
should.equal(fs.existsSync(bitcoinConfig), true);
|
|
||||||
|
|
||||||
var config = JSON.parse(fs.readFileSync(configPath));
|
var config = JSON.parse(fs.readFileSync(configPath));
|
||||||
config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
|
config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
|
||||||
|
@ -103,26 +101,6 @@ describe('#create', function() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will not create bitcoin.conf if it already exists', function() {
|
|
||||||
|
|
||||||
create({
|
|
||||||
cwd: testDir,
|
|
||||||
dirname: 'mynode2',
|
|
||||||
name: 'My Node 2',
|
|
||||||
isGlobal: false,
|
|
||||||
datadir: '../.bitcoin'
|
|
||||||
}, function(err) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bitcoinConfig = testDir + '/.bitcoin/bitcoin.conf';
|
|
||||||
should.equal(fs.existsSync(bitcoinConfig), false);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('will not create a package.json if globally installed', function() {
|
it('will not create a package.json if globally installed', function() {
|
||||||
|
|
||||||
create({
|
create({
|
||||||
|
|
|
@ -75,6 +75,36 @@ describe('Bitcoin Service', function() {
|
||||||
bitcoind._loadConfiguration({datadir: './test'});
|
bitcoind._loadConfiguration({datadir: './test'});
|
||||||
}).should.throw('Txindex option');
|
}).should.throw('Txindex option');
|
||||||
});
|
});
|
||||||
|
it('should set https options if node https options are set', function() {
|
||||||
|
var writeFileSync = function(path, config) {
|
||||||
|
config.should.equal('whitelist=127.0.0.1\ntxindex=1\nrpcssl=1\nrpcsslprivatekeyfile=key.pem\nrpcsslcertificatechainfile=cert.pem\n');
|
||||||
|
};
|
||||||
|
var TestBitcoin = proxyquire('../../lib/services/bitcoind', {
|
||||||
|
fs: {
|
||||||
|
writeFileSync: writeFileSync,
|
||||||
|
readFileSync: readFileSync,
|
||||||
|
existsSync: sinon.stub().returns(false)
|
||||||
|
},
|
||||||
|
mkdirp: {
|
||||||
|
sync: sinon.stub()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var config = {
|
||||||
|
node: {
|
||||||
|
datadir: 'testdir',
|
||||||
|
network: {
|
||||||
|
name: 'regtest'
|
||||||
|
},
|
||||||
|
https: true,
|
||||||
|
httpsOptions: {
|
||||||
|
key: 'key.pem',
|
||||||
|
cert: 'cert.pem'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var bitcoind = new TestBitcoin(config);
|
||||||
|
bitcoind._loadConfiguration({datadir: process.env.HOME + '/.bitcoin'});
|
||||||
|
});
|
||||||
describe('reindex', function() {
|
describe('reindex', function() {
|
||||||
var log = require('../../lib/').log;
|
var log = require('../../lib/').log;
|
||||||
var stub;
|
var stub;
|
||||||
|
|
|
@ -2,17 +2,64 @@
|
||||||
|
|
||||||
var should = require('chai').should();
|
var should = require('chai').should();
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var WebService = require('../../lib/services/web');
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
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 fakeSocketListener = new EventEmitter();
|
||||||
|
var fakeSocket = new EventEmitter();
|
||||||
|
|
||||||
|
fakeSocket.on('test/event1', function(data) {
|
||||||
|
data.should.equal('testdata');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
fakeSocketListener.emit('connection', fakeSocket);
|
||||||
|
|
||||||
|
fakeSocket.emit('subscribe', 'test/event1');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var WebService = proxyquire('../../lib/services/web', {http: httpStub, https: httpsStub, fs: fsStub});
|
||||||
|
|
||||||
describe('WebService', function() {
|
describe('WebService', function() {
|
||||||
var defaultNode = new EventEmitter();
|
var defaultNode = new EventEmitter();
|
||||||
|
|
||||||
describe('#start', function() {
|
describe('#start', function() {
|
||||||
it('should call the callback with no error', function(done) {
|
beforeEach(function() {
|
||||||
|
httpStub.createServer.reset();
|
||||||
|
httpsStub.createServer.reset();
|
||||||
|
});
|
||||||
|
it('should create an http server if no options are specified and node is not configured for https', function(done) {
|
||||||
var web = new WebService({node: defaultNode});
|
var web = new WebService({node: defaultNode});
|
||||||
|
web.deriveHttpsOptions = sinon.spy();
|
||||||
web.start(function(err) {
|
web.start(function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
|
httpStub.createServer.called.should.equal(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an https server if no options are specified and node is configured for https', function(done) {
|
||||||
|
var node = new EventEmitter();
|
||||||
|
node.https = true;
|
||||||
|
|
||||||
|
var web = new WebService({node: node});
|
||||||
|
web.transformHttpsOptions = sinon.spy();
|
||||||
|
web.start(function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
httpsStub.createServer.called.should.equal(true);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -291,4 +338,34 @@ describe('WebService', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#deriveHttpsOptions', function() {
|
||||||
|
it('should read key and cert from files specified', function() {
|
||||||
|
var web = new WebService({
|
||||||
|
node: defaultNode,
|
||||||
|
https: true,
|
||||||
|
httpsOptions: {
|
||||||
|
key: 'key',
|
||||||
|
cert: 'cert'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
web.transformHttpsOptions();
|
||||||
|
web.httpsOptions.key.should.equal('key-buffer');
|
||||||
|
web.httpsOptions.cert.should.equal('cert-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.transformHttpsOptions();
|
||||||
|
}).should.throw('Missing https options');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
Loading…
Reference in New Issue