From 0232776a0d942bf6a14ef393ad2d08412a975a2e Mon Sep 17 00:00:00 2001 From: ROUSSE Kevin Date: Tue, 16 Nov 2021 16:46:18 +0100 Subject: [PATCH 1/3] Enable cookieDomainRewrite by default to host value for cookies --- src/proxy.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/proxy.ts b/src/proxy.ts index cac53c7..584935e 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -1,9 +1,7 @@ import express from 'express'; import { createProxyServer } from 'http-proxy'; -const proxy = createProxyServer({ changeOrigin: true }); - -export const send = ( +const send = ( target: string, req: express.Request, res: express.Response, @@ -19,5 +17,13 @@ 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.web(req, res, { target }, next); }; + +export { send }; From 4440ef46d73d3abc2d7fcc7814fd17ef23266273 Mon Sep 17 00:00:00 2001 From: ROUSSE Kevin Date: Tue, 16 Nov 2021 16:47:40 +0100 Subject: [PATCH 2/3] Add fixRequestBody method call onProxyReq to fix body parsing --- src/fixRequestBody.test.ts | 63 ++++++++++++++++++++++++++++++++++++++ src/fixRequestBody.ts | 33 ++++++++++++++++++++ src/proxy.ts | 3 ++ 3 files changed, 99 insertions(+) create mode 100644 src/fixRequestBody.test.ts create mode 100644 src/fixRequestBody.ts diff --git a/src/fixRequestBody.test.ts b/src/fixRequestBody.test.ts new file mode 100644 index 0000000..6e1c156 --- /dev/null +++ b/src/fixRequestBody.test.ts @@ -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); +}); diff --git a/src/fixRequestBody.ts b/src/fixRequestBody.ts new file mode 100644 index 0000000..96720b5 --- /dev/null +++ b/src/fixRequestBody.ts @@ -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 }; diff --git a/src/proxy.ts b/src/proxy.ts index 584935e..870f394 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -1,6 +1,8 @@ import express from 'express'; import { createProxyServer } from 'http-proxy'; +import { fixRequestBody } from './fixRequestBody'; + const send = ( target: string, req: express.Request, @@ -23,6 +25,7 @@ const send = ( cookieDomainRewrite: req.hostname }); + proxy.on('proxyReq', fixRequestBody); proxy.web(req, res, { target }, next); }; From af81261b3b9e64f7d376cfc1df68ff17c6fc20e4 Mon Sep 17 00:00:00 2001 From: ROUSSE Kevin Date: Tue, 16 Nov 2021 16:48:46 +0100 Subject: [PATCH 3/3] Add removeSecureFromSetCookie on ProxyResponse --- src/proxy.ts | 2 ++ src/removeSecureFromSetCookie.test.ts | 31 +++++++++++++++++++++++++++ src/removeSecureFromSetCookie.ts | 20 +++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/removeSecureFromSetCookie.test.ts create mode 100644 src/removeSecureFromSetCookie.ts diff --git a/src/proxy.ts b/src/proxy.ts index 870f394..84c9b12 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -2,6 +2,7 @@ import express from 'express'; import { createProxyServer } from 'http-proxy'; import { fixRequestBody } from './fixRequestBody'; +import { removeSecureFromSetCookie } from './removeSecureFromSetCookie'; const send = ( target: string, @@ -26,6 +27,7 @@ const send = ( }); proxy.on('proxyReq', fixRequestBody); + proxy.on('proxyRes', removeSecureFromSetCookie); proxy.web(req, res, { target }, next); }; diff --git a/src/removeSecureFromSetCookie.test.ts b/src/removeSecureFromSetCookie.test.ts new file mode 100644 index 0000000..3444f61 --- /dev/null +++ b/src/removeSecureFromSetCookie.test.ts @@ -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({}); +}); diff --git a/src/removeSecureFromSetCookie.ts b/src/removeSecureFromSetCookie.ts new file mode 100644 index 0000000..7c1ecdc --- /dev/null +++ b/src/removeSecureFromSetCookie.ts @@ -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 };