Skip to content

Commit

Permalink
Merge pull request #33 from pmu-tech/feature_handle-cookie-and-body-f…
Browse files Browse the repository at this point in the history
…or-proxy

Fix Set-Cookie header and request body
  • Loading branch information
tkrotoff authored Nov 17, 2021
2 parents 974aeb5 + af81261 commit bb327fc
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 2 deletions.
63 changes: 63 additions & 0 deletions src/fixRequestBody.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import express from 'express';
import http from 'http';

import { fixRequestBody } from './fixRequestBody';

const fakeProxyRequest = () => {
const proxyRequest = new http.ClientRequest('http://some-host');
proxyRequest.emit = () => false; // Otherwise we get "Error: getaddrinfo ENOTFOUND some-host"

return proxyRequest;
};

test('should not write when body is undefined', () => {
const proxyRequest = fakeProxyRequest();

jest.spyOn(proxyRequest, 'setHeader');
jest.spyOn(proxyRequest, 'write');

fixRequestBody(proxyRequest, { body: undefined } as express.Request);

expect(proxyRequest.setHeader).not.toHaveBeenCalled();
expect(proxyRequest.write).not.toHaveBeenCalled();
});

test('should not write when body is empty', () => {
const proxyRequest = fakeProxyRequest();

jest.spyOn(proxyRequest, 'setHeader');
jest.spyOn(proxyRequest, 'write');

fixRequestBody(proxyRequest, { body: {} } as express.Request);

expect(proxyRequest.setHeader).not.toHaveBeenCalled();
expect(proxyRequest.write).not.toHaveBeenCalled();
});

test('should write when body is not empty and Content-Type is application/json', () => {
const proxyRequest = fakeProxyRequest();
proxyRequest.setHeader('content-type', 'application/json; charset=utf-8');

jest.spyOn(proxyRequest, 'setHeader');
jest.spyOn(proxyRequest, 'write');

fixRequestBody(proxyRequest, { body: { someField: 'some value' } } as express.Request);

const expectedBody = JSON.stringify({ someField: 'some value' });
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody);
});

test('should write when body is not empty and Content-Type is application/x-www-form-urlencoded', () => {
const proxyRequest = fakeProxyRequest();
proxyRequest.setHeader('content-type', 'application/x-www-form-urlencoded');

jest.spyOn(proxyRequest, 'setHeader');
jest.spyOn(proxyRequest, 'write');

fixRequestBody(proxyRequest, { body: { someField: 'some value' } } as express.Request);

const expectedBody = 'someField=some+value';
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody);
});
33 changes: 33 additions & 0 deletions src/fixRequestBody.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import http from 'http';

/**
* Fix proxied body if bodyParser is involved.
*
* @see https://github.com/chimurai/http-proxy-middleware/blob/v2.0.1/src/handlers/fix-request-body.ts
* @see https://github.com/chimurai/http-proxy-middleware/issues/320
* @see https://github.com/chimurai/http-proxy-middleware/pull/492
*/
const fixRequestBody = (proxyReq: http.ClientRequest, req: http.IncomingMessage) => {
const requestBody = (req as unknown as Request).body;

if (!requestBody || Object.keys(requestBody).length === 0) {
return;
}

const contentType = proxyReq.getHeader('Content-Type') as string;
const writeBody = (bodyData: string) => {
// deepcode ignore ContentLengthInCode: bodyParser fix
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
proxyReq.write(bodyData);
};

if (contentType && contentType.includes('application/json')) {
writeBody(JSON.stringify(requestBody));
}

if (contentType === 'application/x-www-form-urlencoded') {
writeBody(new URLSearchParams(requestBody as unknown as string).toString());
}
};

export { fixRequestBody };
15 changes: 13 additions & 2 deletions src/proxy.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import express from 'express';
import { createProxyServer } from 'http-proxy';

const proxy = createProxyServer({ changeOrigin: true });
import { fixRequestBody } from './fixRequestBody';
import { removeSecureFromSetCookie } from './removeSecureFromSetCookie';

export const send = (
const send = (
target: string,
req: express.Request,
res: express.Response,
Expand All @@ -19,5 +20,15 @@ export const send = (
//
// [Proxy with express.js](https://stackoverflow.com/q/10435407)

const proxy = createProxyServer({
// Explanations: https://github.com/http-party/node-http-proxy/pull/1130
changeOrigin: true,
cookieDomainRewrite: req.hostname
});

proxy.on('proxyReq', fixRequestBody);
proxy.on('proxyRes', removeSecureFromSetCookie);
proxy.web(req, res, { target }, next);
};

export { send };
31 changes: 31 additions & 0 deletions src/removeSecureFromSetCookie.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { removeSecureFromSetCookie } from './removeSecureFromSetCookie';

test('should remove Secure attribute from Set-Cookie header', () => {
const proxyRes = {
headers: {
'set-cookie': [
'cookie1=ZYADVSQYTDZA1; Secure; SameSite',
'cookie2=ZYADVSQYTDZA2; Secure',
'cookie3=ZYADVSQYTDZA3'
]
}
};

removeSecureFromSetCookie(proxyRes);

expect(proxyRes.headers['set-cookie']).toEqual([
'cookie1=ZYADVSQYTDZA1; SameSite',
'cookie2=ZYADVSQYTDZA2',
'cookie3=ZYADVSQYTDZA3'
]);
});

test('do nothing if no Set-Cookie header', () => {
const proxyRes = {
headers: {}
};

removeSecureFromSetCookie(proxyRes);

expect(proxyRes.headers).toEqual({});
});
20 changes: 20 additions & 0 deletions src/removeSecureFromSetCookie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import http from 'http';

/**
* http-proxy does not remove 'Secure' attribute from Set-Cookie header.
*
* @see https://github.com/http-party/node-http-proxy/issues/1165
* @see https://github.com/http-party/node-http-proxy/pull/1166
*/
const removeSecureFromSetCookie = (proxyRes: { headers: http.IncomingHttpHeaders }) => {
// ["Header names are lower-cased"](https://nodejs.org/dist/latest-v16.x/docs/api/http.html#messageheaders)

if (proxyRes.headers['set-cookie']) {
const cookies = proxyRes.headers['set-cookie'].map(cookie => cookie.replace(/; secure/gi, ''));
/* eslint-disable no-param-reassign */
proxyRes.headers['set-cookie'] = cookies;
/* eslint-enable no-param-reassign */
}
};

export { removeSecureFromSetCookie };

0 comments on commit bb327fc

Please sign in to comment.