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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ app.use('/proxy', proxy('www.google.com', {
}));
```

Note that by default, headers from the proxy response override headers that have previously been set on the user response. With the `userResHeaderDecorator` this can be worked around by combining the headers from `userRes` and `proxyRes` manually.


#### decorateRequest

Expand Down
22 changes: 0 additions & 22 deletions app/steps/copyProxyResHeadersToUserRes.js

This file was deleted.

13 changes: 13 additions & 0 deletions app/steps/decorateUserResHeaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,22 @@ var getHeaders = require('../../lib/getHeaders');

function decorateUserResHeaders(container) {
var resolverFn = container.options.userResHeaderDecorator;
var res = container.user.res;
var rsp = container.proxy.res;

var headers = getHeaders(container.user.res);

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

if (!resolverFn) {
res.set(headers);
return Promise.resolve(container);
}

Expand Down
2 changes: 0 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ var assert = require('assert');
var debug = require('debug')('express-http-proxy');

var buildProxyReq = require('./app/steps/buildProxyReq');
var copyProxyResHeadersToUserRes = require('./app/steps/copyProxyResHeadersToUserRes');
var decorateProxyReqBody = require('./app/steps/decorateProxyReqBody');
var decorateProxyReqOpts = require('./app/steps/decorateProxyReqOpts');
var decorateUserRes = require('./app/steps/decorateUserRes');
Expand Down Expand Up @@ -39,7 +38,6 @@ module.exports = function proxy(host, userOptions) {
.then(prepareProxyReq)
.then(sendProxyRequest)
.then(maybeSkipToNextHandler)
.then(copyProxyResHeadersToUserRes)
.then(decorateUserResHeaders)
.then(decorateUserRes)
.then(sendUserRes)
Expand Down
163 changes: 110 additions & 53 deletions test/decorateUserResHeaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,73 +5,130 @@ var express = require('express');
var request = require('supertest');
var proxy = require('../');

describe('when userResHeaderDecorator is defined', function () {
describe('response headers', function () {
describe('when userResHeaderDecorator is defined', function () {

this.timeout(10000);
this.timeout(10000);

var app;
var serverReference;
var app;
var serverReference;

afterEach(function () {
serverReference.close();
});
afterEach(function () {
serverReference.close();
});

beforeEach(function () {
app = express();
var pTarget = express();
pTarget.use(function (req, res) {
res.header('x-my-not-so-secret-header', 'minnie-mouse');
res.header('x-my-secret-header', 'mighty-mouse');
res.json(req.headers);
beforeEach(function () {
app = express();
var pTarget = express();
app.use(function (req, res, next) {
res.cookie('app-set-cookie1', 'app-value1');
res.cookie('app-set-cookie2', 'app-value2');
next();
});
pTarget.use(function (req, res) {
res.header('x-my-not-so-secret-header', 'minnie-mouse');
res.header('x-my-secret-header', 'mighty-mouse');
res.cookie('pTarget-set-cookie1', 'pTarget-value1');
res.cookie('pTarget-set-cookie2', 'pTarget-value2');
res.json(req.headers);
});
serverReference = pTarget.listen(12345);
});
serverReference = pTarget.listen(12345);
});

afterEach(function () {
serverReference.close();
});
afterEach(function () {
serverReference.close();
});

it('can delete a header', function (done) {
it('can delete a header', function (done) {

app.use('/proxy', proxy('http://127.0.0.1:12345', {
userResHeaderDecorator: function (headers /*, userReq, userRes, proxyReq, proxyRes */) {
delete headers['x-my-secret-header'];
return headers;
}
}));

app.use(function (req, res) {
res.sendStatus(200);
});

request(app)
.get('/proxy')
.expect(function (res) {
assert(Object.keys(res.headers).indexOf('x-my-not-so-secret-header') > -1);
assert(Object.keys(res.headers).indexOf('x-my-secret-header') === -1);
})
.end(done);
});

app.use('/proxy', proxy('http://127.0.0.1:12345', {
userResHeaderDecorator: function (headers /*, userReq, userRes, proxyReq, proxyRes */) {
delete headers['x-my-secret-header'];
return headers;
}
}));
it('provides an interface for updating headers', function (done) {

app.use('/proxy', proxy('http://127.0.0.1:12345', {
userResHeaderDecorator: function (headers /*, userReq, userRes, proxyReq, proxyRes */) {
headers.boltedonheader = 'franky';
return headers;
}
}));

app.use(function (req, res) {
res.sendStatus(200);
});

request(app)
.get('/proxy')
.expect(function (res) {
assert(res.headers.boltedonheader === 'franky');
})
.end(done);
});

app.use(function (req, res) {
res.sendStatus(200);
it('does not overwrite userRes headers', function (done) {

app.use('/proxy', proxy('http://127.0.0.1:12345', {
// eslint-disable-next-line no-unused-vars
userResHeaderDecorator: function (headers, userReq, userRes, proxyReq, proxyRes) {
headers['set-cookie'] = [...userRes.getHeaders()['set-cookie'], ...proxyRes.headers['set-cookie']];
return headers;
}
}));

app.use(function (req, res) {
res.sendStatus(200);
});

request(app)
.get('/proxy')
.expect(function (res) {
assert.deepStrictEqual(
res.headers['set-cookie'],
[
'app-set-cookie1=app-value1; Path=/',
'app-set-cookie2=app-value2; Path=/',
'pTarget-set-cookie1=pTarget-value1; Path=/',
'pTarget-set-cookie2=pTarget-value2; Path=/'
]
);
})
.end(done);
});

request(app)
.get('/proxy')
.expect(function (res) {
assert(Object.keys(res.headers).indexOf('x-my-not-so-secret-header') > -1);
assert(Object.keys(res.headers).indexOf('x-my-secret-header') === -1);
})
.end(done);
});
it('overwrites res headers when userResHeaderDecorator is not set', function (done) {

it('provides an interface for updating headers', function (done) {
app.use('/proxy', proxy('http://127.0.0.1:12345'));

app.use('/proxy', proxy('http://127.0.0.1:12345', {
userResHeaderDecorator: function (headers /*, userReq, userRes, proxyReq, proxyRes */) {
headers.boltedonheader = 'franky';
return headers;
}
}));
app.use(function (req, res) {
res.sendStatus(200);
});

app.use(function (req, res) {
res.sendStatus(200);
request(app)
.get('/proxy')
.expect(function (res) {
assert.deepStrictEqual(
res.headers['set-cookie'],
['pTarget-set-cookie1=pTarget-value1; Path=/', 'pTarget-set-cookie2=pTarget-value2; Path=/']
);
})
.end(done);
});

request(app)
.get('/proxy')
.expect(function (res) {
assert(res.headers.boltedonheader === 'franky');
})
.end(done);
});

});