Skip to content

Commit f6152d0

Browse files
committed
Merge pull request #33 from pmu-tech/feature_handle-cookie-and-body-for-proxy
- Rewrite cookie domain - Fix request body - Remove Secure from Set-Cookie header
2 parents 974aeb5 + af81261 commit f6152d0

5 files changed

+160
-2
lines changed

src/fixRequestBody.test.ts

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import express from 'express';
2+
import http from 'http';
3+
4+
import { fixRequestBody } from './fixRequestBody';
5+
6+
const fakeProxyRequest = () => {
7+
const proxyRequest = new http.ClientRequest('http://some-host');
8+
proxyRequest.emit = () => false; // Otherwise we get "Error: getaddrinfo ENOTFOUND some-host"
9+
10+
return proxyRequest;
11+
};
12+
13+
test('should not write when body is undefined', () => {
14+
const proxyRequest = fakeProxyRequest();
15+
16+
jest.spyOn(proxyRequest, 'setHeader');
17+
jest.spyOn(proxyRequest, 'write');
18+
19+
fixRequestBody(proxyRequest, { body: undefined } as express.Request);
20+
21+
expect(proxyRequest.setHeader).not.toHaveBeenCalled();
22+
expect(proxyRequest.write).not.toHaveBeenCalled();
23+
});
24+
25+
test('should not write when body is empty', () => {
26+
const proxyRequest = fakeProxyRequest();
27+
28+
jest.spyOn(proxyRequest, 'setHeader');
29+
jest.spyOn(proxyRequest, 'write');
30+
31+
fixRequestBody(proxyRequest, { body: {} } as express.Request);
32+
33+
expect(proxyRequest.setHeader).not.toHaveBeenCalled();
34+
expect(proxyRequest.write).not.toHaveBeenCalled();
35+
});
36+
37+
test('should write when body is not empty and Content-Type is application/json', () => {
38+
const proxyRequest = fakeProxyRequest();
39+
proxyRequest.setHeader('content-type', 'application/json; charset=utf-8');
40+
41+
jest.spyOn(proxyRequest, 'setHeader');
42+
jest.spyOn(proxyRequest, 'write');
43+
44+
fixRequestBody(proxyRequest, { body: { someField: 'some value' } } as express.Request);
45+
46+
const expectedBody = JSON.stringify({ someField: 'some value' });
47+
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
48+
expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody);
49+
});
50+
51+
test('should write when body is not empty and Content-Type is application/x-www-form-urlencoded', () => {
52+
const proxyRequest = fakeProxyRequest();
53+
proxyRequest.setHeader('content-type', 'application/x-www-form-urlencoded');
54+
55+
jest.spyOn(proxyRequest, 'setHeader');
56+
jest.spyOn(proxyRequest, 'write');
57+
58+
fixRequestBody(proxyRequest, { body: { someField: 'some value' } } as express.Request);
59+
60+
const expectedBody = 'someField=some+value';
61+
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
62+
expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody);
63+
});

src/fixRequestBody.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import http from 'http';
2+
3+
/**
4+
* Fix proxied body if bodyParser is involved.
5+
*
6+
* @see https://github.com/chimurai/http-proxy-middleware/blob/v2.0.1/src/handlers/fix-request-body.ts
7+
* @see https://github.com/chimurai/http-proxy-middleware/issues/320
8+
* @see https://github.com/chimurai/http-proxy-middleware/pull/492
9+
*/
10+
const fixRequestBody = (proxyReq: http.ClientRequest, req: http.IncomingMessage) => {
11+
const requestBody = (req as unknown as Request).body;
12+
13+
if (!requestBody || Object.keys(requestBody).length === 0) {
14+
return;
15+
}
16+
17+
const contentType = proxyReq.getHeader('Content-Type') as string;
18+
const writeBody = (bodyData: string) => {
19+
// deepcode ignore ContentLengthInCode: bodyParser fix
20+
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
21+
proxyReq.write(bodyData);
22+
};
23+
24+
if (contentType && contentType.includes('application/json')) {
25+
writeBody(JSON.stringify(requestBody));
26+
}
27+
28+
if (contentType === 'application/x-www-form-urlencoded') {
29+
writeBody(new URLSearchParams(requestBody as unknown as string).toString());
30+
}
31+
};
32+
33+
export { fixRequestBody };

src/proxy.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import express from 'express';
22
import { createProxyServer } from 'http-proxy';
33

4-
const proxy = createProxyServer({ changeOrigin: true });
4+
import { fixRequestBody } from './fixRequestBody';
5+
import { removeSecureFromSetCookie } from './removeSecureFromSetCookie';
56

6-
export const send = (
7+
const send = (
78
target: string,
89
req: express.Request,
910
res: express.Response,
@@ -19,5 +20,15 @@ export const send = (
1920
//
2021
// [Proxy with express.js](https://stackoverflow.com/q/10435407)
2122

23+
const proxy = createProxyServer({
24+
// Explanations: https://github.com/http-party/node-http-proxy/pull/1130
25+
changeOrigin: true,
26+
cookieDomainRewrite: req.hostname
27+
});
28+
29+
proxy.on('proxyReq', fixRequestBody);
30+
proxy.on('proxyRes', removeSecureFromSetCookie);
2231
proxy.web(req, res, { target }, next);
2332
};
33+
34+
export { send };

src/removeSecureFromSetCookie.test.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { removeSecureFromSetCookie } from './removeSecureFromSetCookie';
2+
3+
test('should remove Secure attribute from Set-Cookie header', () => {
4+
const proxyRes = {
5+
headers: {
6+
'set-cookie': [
7+
'cookie1=ZYADVSQYTDZA1; Secure; SameSite',
8+
'cookie2=ZYADVSQYTDZA2; Secure',
9+
'cookie3=ZYADVSQYTDZA3'
10+
]
11+
}
12+
};
13+
14+
removeSecureFromSetCookie(proxyRes);
15+
16+
expect(proxyRes.headers['set-cookie']).toEqual([
17+
'cookie1=ZYADVSQYTDZA1; SameSite',
18+
'cookie2=ZYADVSQYTDZA2',
19+
'cookie3=ZYADVSQYTDZA3'
20+
]);
21+
});
22+
23+
test('do nothing if no Set-Cookie header', () => {
24+
const proxyRes = {
25+
headers: {}
26+
};
27+
28+
removeSecureFromSetCookie(proxyRes);
29+
30+
expect(proxyRes.headers).toEqual({});
31+
});

src/removeSecureFromSetCookie.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import http from 'http';
2+
3+
/**
4+
* http-proxy does not remove 'Secure' attribute from Set-Cookie header.
5+
*
6+
* @see https://github.com/http-party/node-http-proxy/issues/1165
7+
* @see https://github.com/http-party/node-http-proxy/pull/1166
8+
*/
9+
const removeSecureFromSetCookie = (proxyRes: { headers: http.IncomingHttpHeaders }) => {
10+
// ["Header names are lower-cased"](https://nodejs.org/dist/latest-v16.x/docs/api/http.html#messageheaders)
11+
12+
if (proxyRes.headers['set-cookie']) {
13+
const cookies = proxyRes.headers['set-cookie'].map(cookie => cookie.replace(/; secure/gi, ''));
14+
/* eslint-disable no-param-reassign */
15+
proxyRes.headers['set-cookie'] = cookies;
16+
/* eslint-enable no-param-reassign */
17+
}
18+
};
19+
20+
export { removeSecureFromSetCookie };

0 commit comments

Comments
 (0)