Skip to content
This repository has been archived by the owner on Jan 7, 2018. It is now read-only.

Commit

Permalink
Merge pull request #9 from NREL/response-headers
Browse files Browse the repository at this point in the history
Add the ability to modify response headers (can be used for CORS setup)
  • Loading branch information
GUI committed Mar 29, 2015
2 parents afa21cc + 9c275d7 commit f090da8
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib/gatekeeper/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ module.exports.rateLimit = require('./middleware/rate_limit');
module.exports.bufferRequest = bufferedRequest.bufferRequest;
module.exports.proxyBufferedRequest = bufferedRequest.proxyBufferedRequest;
module.exports.rewriteRequest = require('./middleware/rewrite_request');
module.exports.rewriteResponse = require('./middleware/rewrite_response');
57 changes: 57 additions & 0 deletions lib/gatekeeper/middleware/rewrite_response.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict';

var _ = require('lodash');

var RewriteResponse = function() {
this.initialize.apply(this, arguments);
};

_.extend(RewriteResponse.prototype, {
initialize: function() {
},

handleResponse: function(request, response, next) {
// Wait until response.writeHead() is called to modify the response so we
// can ensure that all the headers have been received, but not yet sent.
var origWriteHead = response.writeHead;
response.writeHead = function() {
this.setDefaultHeaders(request, response);
this.setOverrideHeaders(request, response);

return origWriteHead.apply(response, arguments);
}.bind(this);

next();
},

setDefaultHeaders: function(request, response) {
var headers = request.apiUmbrellaGatekeeper.settings.default_response_headers;
if(headers) {
for(var i = 0, len = headers.length; i < len; i++) {
var header = headers[i];
var existingValue = response.getHeader(header.key);
if(!existingValue) {
response.setHeader(header.key, header.value);
}
}
}
},

setOverrideHeaders: function(request, response) {
var headers = request.apiUmbrellaGatekeeper.settings.override_response_headers;
if(headers) {
for(var i = 0, len = headers.length; i < len; i++) {
var header = headers[i];
response.setHeader(header.key, header.value);
}
}
},
});

module.exports = function rewriteResponse() {
var middleware = new RewriteResponse();

return function(request, response, next) {
middleware.handleResponse(request, response, next);
};
};
1 change: 1 addition & 0 deletions lib/gatekeeper/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ _.extend(Worker.prototype, {
middleware.rateLimit(this),
middleware.rewriteRequest(),
middleware.proxyBufferedRequest(this.server.proxy),
middleware.rewriteResponse(),
];

this.stack = httpProxy.stack(this.middlewares, this.server.proxy);
Expand Down
1 change: 0 additions & 1 deletion test/server/api_matcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,6 @@ describe('ApiUmbrellaGatekeper', function() {
describe('mismatched case sensitivity', function() {
shared.itBehavesLikeGatekeeperBlocked('/info/SPECIFIC/', 404, 'NOT_FOUND');
});

});
});

Expand Down
157 changes: 157 additions & 0 deletions test/server/response_rewriting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
'use strict';

require('../test_helper');

var request = require('request');

describe('response rewriting', function() {
describe('setting default response headers', function() {
shared.runServer({
apis: [
{
frontend_host: 'localhost',
backend_host: 'example.com',
url_matches: [
{
frontend_prefix: '/',
backend_prefix: '/',
}
],
settings: {
default_response_headers: [
{ key: 'X-Add1', value: 'test1' },
{ key: 'X-Add2', value: 'test2' },
{ key: 'X-Existing1', value: 'test3' },
{ key: 'X-EXISTING2', value: 'test4' },
{ key: 'x-existing3', value: 'test5' },
],
},
sub_settings: [
{
http_method: 'any',
regex: '^/headers/sub',
settings: {
default_response_headers: [
{ key: 'X-Add2', value: 'overridden' },
],
},
},
],
},
],
});

describe('default', function() {
it('sets new header values', function(done) {
request.get('http://localhost:9333/headers/?api_key=' + this.apiKey, function(error, response) {
should.not.exist(error);
response.statusCode.should.eql(200);
response.headers['x-add1'].should.eql('test1');
response.headers['x-add2'].should.eql('test2');
done();
});
});

it('leaves existing headers (case insensitive)', function(done) {
request.get('http://localhost:9333/headers/?api_key=' + this.apiKey, function(error, response) {
should.not.exist(error);
response.statusCode.should.eql(200);
response.headers['x-existing1'].should.eql('existing1');
response.headers['x-existing2'].should.eql('existing2');
response.headers['x-existing3'].should.eql('existing3');
done();
});
});
});

describe('sub-url match', function() {
it('overrides the default header settings', function(done) {
request.get('http://localhost:9333/headers/sub/?api_key=' + this.apiKey, function(error, response) {
should.not.exist(error);
response.statusCode.should.eql(200);
should.not.exist(response.headers['x-add1']);
response.headers['x-add2'].should.eql('overridden');
response.headers['x-existing1'].should.eql('existing1');
response.headers['x-existing2'].should.eql('existing2');
response.headers['x-existing3'].should.eql('existing3');
done();
});
});
});
});

describe('setting override response headers', function() {
shared.runServer({
apis: [
{
frontend_host: 'localhost',
backend_host: 'example.com',
url_matches: [
{
frontend_prefix: '/',
backend_prefix: '/',
}
],
settings: {
override_response_headers: [
{ key: 'X-Add1', value: 'test1' },
{ key: 'X-Add2', value: 'test2' },
{ key: 'X-Existing1', value: 'test3' },
{ key: 'X-EXISTING2', value: 'test4' },
{ key: 'x-existing3', value: 'test5' },
],
},
sub_settings: [
{
http_method: 'any',
regex: '^/headers/sub',
settings: {
override_response_headers: [
{ key: 'X-Existing3', value: 'overridden' },
],
},
},
],
},
],
});

describe('default', function() {
it('sets new header values', function(done) {
request.get('http://localhost:9333/headers/?api_key=' + this.apiKey, function(error, response) {
should.not.exist(error);
response.statusCode.should.eql(200);
response.headers['x-add1'].should.eql('test1');
response.headers['x-add2'].should.eql('test2');
done();
});
});

it('overrides existing headers (case insensitive)', function(done) {
request.get('http://localhost:9333/headers/?api_key=' + this.apiKey, function(error, response) {
should.not.exist(error);
response.statusCode.should.eql(200);
response.headers['x-existing1'].should.eql('test3');
response.headers['x-existing2'].should.eql('test4');
response.headers['x-existing3'].should.eql('test5');
done();
});
});
});

describe('sub-url match', function() {
it('overrides the default header settings', function(done) {
request.get('http://localhost:9333/headers/sub/?api_key=' + this.apiKey, function(error, response) {
should.not.exist(error);
response.statusCode.should.eql(200);
should.not.exist(response.headers['x-add1']);
should.not.exist(response.headers['x-add2']);
response.headers['x-existing1'].should.eql('existing1');
response.headers['x-existing2'].should.eql('existing2');
response.headers['x-existing3'].should.eql('overridden');
done();
});
});
});
});
});
7 changes: 7 additions & 0 deletions test/support/example_backend_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ app.get('/chunked', function(req, res) {
}, 100);
});

app.get('/headers/*', function(req, res) {
res.set('X-Existing1', 'existing1');
res.set('x-existing2', 'existing2');
res.set('X-EXISTING3', 'existing3');
res.send('Hello World');
});

app.all('/info/*', function(req, res) {
var rawUrl = req.protocol + '://' + req.hostname + req.url;
res.json({
Expand Down

0 comments on commit f090da8

Please sign in to comment.