Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
'use strict';

var assert = require('assert');
var url = require('url');
var http = require('http');
Expand All @@ -18,6 +17,8 @@ module.exports = function proxy(host, options) {
/**
* Function :: intercept(targetResponse, data, res, req, function(err, json, sent));
*/
var readCache = options.readCache || defaultReadCache;
var writeCache = options.writeCache;
var intercept = options.intercept;
var decorateRequest = options.decorateRequest;
var forwardPath = options.forwardPath || defaultForwardPath;
Expand All @@ -28,13 +29,33 @@ module.exports = function proxy(host, options) {

return function handleProxy(req, res, next) {
if (!filter(req, res)) { return next(); }

forwardPathAsync(req, res)
.then(function(path) {
var readPromise = readCache ? readCache(req) : null;
// If a cached value exists, use it
if (readPromise) {
readPromise.then(function(cachedRes) {
copyResponse(cachedRes, res);
if (!res.headersSent) {
res.send(cachedRes.data);
}
});
} else {
forwardPathAsync(req, res)
.then(function(path) {
proxyWithResolvedPath(req, res, next, path);
});
}
};

// Copies status and headers from source response to the target response
function copyResponse(source, target) {
target.status(source.statusCode);
Object.keys(source.headers)
.filter(function(item) { return item !== 'transfer-encoding'; })
.forEach(function(item) {
target.set(item, source.headers[item]);
});
}

function proxyWithResolvedPath(req, res, next, path) {
parsedHost = parsedHost || parseHost(host, req, options);

Expand Down Expand Up @@ -95,6 +116,9 @@ module.exports = function proxy(host, options) {
rsp.on('end', function() {

var rspData = Buffer.concat(chunks, chunkLength(chunks));
if (writeCache) {
writeCache(req, {statusCode: rsp.statusCode, headers: rsp.headers, data: rspData});
}

if (intercept) {
rspData = maybeUnzipResponse(rspData, res);
Expand Down Expand Up @@ -136,12 +160,7 @@ module.exports = function proxy(host, options) {
});

if (!res.headersSent) {
res.status(rsp.statusCode);
Object.keys(rsp.headers)
.filter(function(item) { return item !== 'transfer-encoding'; })
.forEach(function(item) {
res.set(item, rsp.headers[item]);
});
copyResponse(rsp, res);
}
});

Expand Down Expand Up @@ -248,6 +267,10 @@ function defaultForwardPath(req) {
return url.parse(req.url).path;
}

function defaultReadCache() {
return null; // Default is always a cache-miss.
}

function bodyEncoding(options) {


Expand Down
45 changes: 45 additions & 0 deletions test/readCache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
var assert = require('assert');
var express = require('express');
var request = require('supertest');
var proxy = require('../');
var promise = require('es6-promise');


describe('readCache', function() {
'use strict';
this.timeout(10000);

var app;

beforeEach(function() {
app = express();
app.use(proxy('httpbin.org'));
});

it('should provide a cached response as though it came from the host', function(done) {
app = express();
app.use(proxy('httpbin.org',{
readCache: function() {
return new promise.Promise(function(resolve) {
resolve({
headers: {
'x-president': 'Obama'
},
statusCode: 200,
data: {
isCached: 'yes'
}
});
});
}
}));
request(app)
.get('/get')
.end(function(err, res) {
if (err) { return done(err); }
assert(res.header['x-president'],'Obama');
assert.equal(res.body.isCached, 'yes');
done(err);
});
});
});
30 changes: 30 additions & 0 deletions test/writeCache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
var assert = require('assert');
var express = require('express');
var request = require('supertest');
var proxy = require('../');

describe('writeCache', function() {
'use strict';
this.timeout(10000);

var app;

it('should receive the request and cache data', function(done) {
app = express();
app.use(proxy('httpbin.org',{
cacheWrite: function(req,cacheData) {
assert.equal (cacheData.data.url,'http://httpbin.org/get');
assert.equal (cacheData.headers['Content-Type'], 'application/json');
assert.equal (cacheData.status,'200');
}
}));
request(app)
.get('/get')
.end(function(err, res) {
if (err) { return done(err); }
assert(/node-superagent/.test(res.body.headers['User-Agent']));
assert.equal(res.body.url, 'http://httpbin.org/get');
done(err);
});
});
});