Skip to content

Commit

Permalink
avoid overwriting userRes headers when using userResHeaderDecorator
Browse files Browse the repository at this point in the history
  • Loading branch information
fabb committed Aug 17, 2024
1 parent b05cb04 commit 7c2dc40
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 77 deletions.
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);
});

});

0 comments on commit 7c2dc40

Please sign in to comment.