Added configurable caching.

This commit is contained in:
Braydon Fuller 2015-10-23 15:20:20 -04:00
parent 2a14955d74
commit 94f584f792
3 changed files with 167 additions and 22 deletions

View File

@ -3,7 +3,6 @@
var common = require('./common');
var async = require('async');
var bitcore = require('bitcore-lib');
var BufferUtil = bitcore.util.buffer;
var pools = require('../pools.json');
var BN = bitcore.crypto.BN;

View File

@ -15,6 +15,15 @@ var $ = bitcore.util.preconditions;
var Transaction = bitcore.Transaction;
var EventEmitter = require('events').EventEmitter;
/**
* A service for Bitcore to enable HTTP routes to query information about the blockchain.
*
* @param {Object} options
* @param {Boolean} options.enableCache - This will enable cache-control headers
* @param {Number} options.cacheShortSeconds - The time to cache short lived cache responses.
* @param {Number} options.cacheLongSeconds - The time to cache long lived cache responses.
* @param {String} options.routePrefix - The URL route prefix
*/
var InsightAPI = function(options) {
BaseService.call(this, options);
@ -25,6 +34,13 @@ var InsightAPI = function(options) {
inv: []
};
if (!_.isUndefined(options.enableCache)) {
$.checkArgument(_.isBoolean(options.enableCache));
this.enableCache = options.enableCache;
}
this.cacheShortSeconds = options.cacheShortSeconds;
this.cacheLongSeconds = options.cacheLongSeconds;
if (!_.isUndefined(options.routePrefix)) {
this.routePrefix = options.routePrefix;
} else {
@ -38,6 +54,26 @@ InsightAPI.dependencies = ['address', 'web'];
inherits(InsightAPI, BaseService);
InsightAPI.prototype.cache = function(maxAge) {
var self = this;
return function(req, res, next) {
if (self.enableCache) {
res.header('Cache-Control', 'public, max-age=' + maxAge);
}
next();
};
};
InsightAPI.prototype.cacheShort = function() {
var seconds = this.cacheShortSeconds || 30; // thirty seconds
return this.cache(seconds);
};
InsightAPI.prototype.cacheLong = function() {
var seconds = this.cacheLongSeconds || 86400; // one day
return this.cache(seconds);
};
InsightAPI.prototype.getRoutePrefix = function() {
return this.routePrefix;
};
@ -49,6 +85,7 @@ InsightAPI.prototype.start = function(callback) {
};
InsightAPI.prototype.setupRoutes = function(app) {
//Enable CORS
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
@ -58,48 +95,47 @@ InsightAPI.prototype.setupRoutes = function(app) {
//Block routes
var blocks = new BlockController(this.node);
app.get('/blocks', blocks.list.bind(blocks));
app.get('/blocks', this.cacheShort(), blocks.list.bind(blocks));
app.get('/block/:blockHash', blocks.show.bind(blocks));
app.get('/block/:blockHash', this.cacheLong(), blocks.show.bind(blocks));
app.param('blockHash', blocks.block.bind(blocks));
app.get('/block-index/:height', blocks.blockIndex.bind(blocks));
app.get('/block-index/:height', this.cacheLong(), blocks.blockIndex.bind(blocks));
app.param('height', blocks.blockIndex.bind(blocks));
// Transaction routes
var transactions = new TxController(this.node);
app.get('/tx/:txid', transactions.show.bind(transactions));
app.get('/tx/:txid', this.cacheLong(), transactions.show.bind(transactions));
app.param('txid', transactions.transaction.bind(transactions));
app.get('/txs', transactions.list.bind(transactions));
app.get('/txs', this.cacheShort(), transactions.list.bind(transactions));
app.post('/tx/send', transactions.send.bind(transactions));
// Raw Routes
app.get('/rawtx/:txid', transactions.showRaw.bind(transactions));
app.get('/rawtx/:txid', this.cacheLong(), transactions.showRaw.bind(transactions));
app.param('txid', transactions.rawTransaction.bind(transactions));
// Address routes
var addresses = new AddressController(this.node);
app.get('/addr/:addr', addresses.checkAddr.bind(addresses), addresses.show.bind(addresses));
app.get('/addr/:addr/utxo', addresses.checkAddr.bind(addresses), addresses.utxo.bind(addresses));
app.get('/addrs/:addrs/utxo', addresses.checkAddrs.bind(addresses), addresses.multiutxo.bind(addresses));
app.post('/addrs/utxo', addresses.checkAddrs.bind(addresses), addresses.multiutxo.bind(addresses));
app.get('/addrs/:addrs/txs', addresses.checkAddrs.bind(addresses), addresses.multitxs.bind(addresses));
app.post('/addrs/txs', addresses.checkAddrs.bind(addresses), addresses.multitxs.bind(addresses));
app.get('/addr/:addr', this.cacheShort(), addresses.checkAddr.bind(addresses), addresses.show.bind(addresses));
app.get('/addr/:addr/utxo', this.cacheShort(), addresses.checkAddr.bind(addresses), addresses.utxo.bind(addresses));
app.get('/addrs/:addrs/utxo', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multiutxo.bind(addresses));
app.post('/addrs/utxo', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multiutxo.bind(addresses));
app.get('/addrs/:addrs/txs', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multitxs.bind(addresses));
app.post('/addrs/txs', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multitxs.bind(addresses));
// Address property routes
app.get('/addr/:addr/balance', addresses.checkAddr.bind(addresses), addresses.balance.bind(addresses));
app.get('/addr/:addr/totalReceived', addresses.checkAddr.bind(addresses), addresses.totalReceived.bind(addresses));
app.get('/addr/:addr/totalSent', addresses.checkAddr.bind(addresses), addresses.totalSent.bind(addresses));
app.get('/addr/:addr/unconfirmedBalance', addresses.checkAddr.bind(addresses), addresses.unconfirmedBalance.bind(addresses));
app.get('/addr/:addr/balance', this.cacheShort(), addresses.checkAddr.bind(addresses), addresses.balance.bind(addresses));
app.get('/addr/:addr/totalReceived', this.cacheShort(), addresses.checkAddr.bind(addresses), addresses.totalReceived.bind(addresses));
app.get('/addr/:addr/totalSent', this.cacheShort(), addresses.checkAddr.bind(addresses), addresses.totalSent.bind(addresses));
app.get('/addr/:addr/unconfirmedBalance', this.cacheShort(), addresses.checkAddr.bind(addresses), addresses.unconfirmedBalance.bind(addresses));
// Status route
var status = new StatusController(this.node);
app.get('/status', status.show.bind(status));
app.get('/sync', status.sync.bind(status));
app.get('/peer', status.peer.bind(status));
app.get('/version', status.version.bind(status));
app.get('/status', this.cacheShort(), status.show.bind(status));
app.get('/sync', this.cacheShort(), status.sync.bind(status));
app.get('/peer', this.cacheShort(), status.peer.bind(status));
app.get('/version', this.cacheShort(), status.version.bind(status));
// Address routes
var messages = new MessagesController(this.node);

110
test/index.js Normal file
View File

@ -0,0 +1,110 @@
'use strict';
var should = require('should');
var sinon = require('sinon');
var InsightAPI = require('../lib/index');
describe('Index', function() {
describe('#cache', function() {
it('will set cache control header', function(done) {
var index = new InsightAPI({
enableCache: true
});
var req = {};
var res = {
header: sinon.stub()
};
var middle = index.cache(10);
middle(req, res, function() {
res.header.callCount.should.equal(1);
res.header.args[0][0].should.equal('Cache-Control');
res.header.args[0][1].should.equal('public, max-age=10');
done();
});
});
it('will NOT set cache control header', function(done) {
var index = new InsightAPI({
enableCache: false
});
var req = {};
var res = {
header: sinon.stub()
};
var middle = index.cache(10);
middle(req, res, function() {
res.header.callCount.should.equal(0);
done();
});
});
});
describe('#cacheShort', function() {
it('will set SHORT cache control header', function(done) {
var index = new InsightAPI({
enableCache: true,
cacheShortSeconds: 35
});
var req = {};
var res = {
header: sinon.stub()
};
var middle = index.cacheShort();
middle(req, res, function() {
res.header.callCount.should.equal(1);
res.header.args[0][0].should.equal('Cache-Control');
res.header.args[0][1].should.equal('public, max-age=35');
done();
});
});
it('will set SHORT DEFAULT cache control header', function(done) {
var index = new InsightAPI({
enableCache: true
});
var req = {};
var res = {
header: sinon.stub()
};
var middle = index.cacheShort();
middle(req, res, function() {
res.header.callCount.should.equal(1);
res.header.args[0][0].should.equal('Cache-Control');
res.header.args[0][1].should.equal('public, max-age=30');
done();
});
});
});
describe('#cacheLong', function() {
it('will set LONG cache control header', function(done) {
var index = new InsightAPI({
enableCache: true,
cacheLongSeconds: 86400000
});
var req = {};
var res = {
header: sinon.stub()
};
var middle = index.cacheLong();
middle(req, res, function() {
res.header.callCount.should.equal(1);
res.header.args[0][0].should.equal('Cache-Control');
res.header.args[0][1].should.equal('public, max-age=86400000');
done();
});
});
it('will set LONG DEFAULT cache control header', function(done) {
var index = new InsightAPI({
enableCache: true
});
var req = {};
var res = {
header: sinon.stub()
};
var middle = index.cacheLong();
middle(req, res, function() {
res.header.callCount.should.equal(1);
res.header.args[0][0].should.equal('Cache-Control');
res.header.args[0][1].should.equal('public, max-age=86400');
done();
});
});
});
});