From a12a59df42507659e536ce60c04ee7f97dd5aad5 Mon Sep 17 00:00:00 2001 From: The Mark Date: Wed, 15 Feb 2023 19:01:19 +0800 Subject: [PATCH 1/2] fix(utils): fixRequestBody support `multipart/form-data` --- src/handlers/fix-request-body.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/handlers/fix-request-body.ts b/src/handlers/fix-request-body.ts index 2767fe08..44be5471 100644 --- a/src/handlers/fix-request-body.ts +++ b/src/handlers/fix-request-body.ts @@ -23,6 +23,15 @@ export function fixRequestBody( proxyReq.write(bodyData); }; + const handlerFormDataBodyData = (contentType: string, data: any) => { + const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1'); + let str = ''; + for (const [key, value] of Object.entries(data)) { + str += `--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`; + } + return str; + }; + if (contentType && contentType.includes('application/json')) { writeBody(JSON.stringify(requestBody)); } @@ -30,4 +39,8 @@ export function fixRequestBody( if (contentType && contentType.includes('application/x-www-form-urlencoded')) { writeBody(querystring.stringify(requestBody)); } + + if (contentType && contentType.includes('multipart/form-data')) { + writeBody(handlerFormDataBodyData(contentType, requestBody)); + } } From 526ae10f2d19b4f74b538aa438ab57bb6f7579e4 Mon Sep 17 00:00:00 2001 From: The Mark Date: Thu, 16 Feb 2023 10:53:39 +0800 Subject: [PATCH 2/2] fix(utils): add unit test and split func --- src/handlers/fix-request-body.ts | 24 +++++++----- test/unit/fix-request-body.spec.ts | 59 +++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/handlers/fix-request-body.ts b/src/handlers/fix-request-body.ts index 44be5471..c37f629c 100644 --- a/src/handlers/fix-request-body.ts +++ b/src/handlers/fix-request-body.ts @@ -23,15 +23,6 @@ export function fixRequestBody( proxyReq.write(bodyData); }; - const handlerFormDataBodyData = (contentType: string, data: any) => { - const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1'); - let str = ''; - for (const [key, value] of Object.entries(data)) { - str += `--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`; - } - return str; - }; - if (contentType && contentType.includes('application/json')) { writeBody(JSON.stringify(requestBody)); } @@ -44,3 +35,18 @@ export function fixRequestBody( writeBody(handlerFormDataBodyData(contentType, requestBody)); } } + +/** + * format FormData data + * @param contentType + * @param data + * @returns + */ +function handlerFormDataBodyData(contentType: string, data: any) { + const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1'); + let str = ''; + for (const [key, value] of Object.entries(data)) { + str += `--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`; + } + return str; +} diff --git a/test/unit/fix-request-body.spec.ts b/test/unit/fix-request-body.spec.ts index fe254318..0562a1e7 100644 --- a/test/unit/fix-request-body.spec.ts +++ b/test/unit/fix-request-body.spec.ts @@ -1,7 +1,6 @@ import { Socket } from 'net'; import { ClientRequest, IncomingMessage } from 'http'; import * as querystring from 'querystring'; - import { fixRequestBody, BodyParserLikeRequest } from '../../src/handlers/fix-request-body'; const fakeProxyRequest = (): ClientRequest => { @@ -17,6 +16,15 @@ const createRequestWithBody = (body: unknown): BodyParserLikeRequest => { return req; }; +const handlerFormDataBodyData = (contentType: string, data: { [key: string]: any }) => { + const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1'); + let str = ''; + for (const [key, value] of Object.entries(data)) { + str += `--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`; + } + return str; +}; + describe('fixRequestBody', () => { it('should not write when body is undefined', () => { const proxyRequest = fakeProxyRequest(); @@ -57,6 +65,55 @@ describe('fixRequestBody', () => { expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody); }); + it('should write when body is not empty and Content-Type is multipart/form-data', () => { + const proxyRequest = fakeProxyRequest(); + proxyRequest.setHeader('content-type', 'multipart/form-data'); + + jest.spyOn(proxyRequest, 'setHeader'); + jest.spyOn(proxyRequest, 'write'); + + fixRequestBody(proxyRequest, createRequestWithBody({ someField: 'some value' })); + + const expectedBody = handlerFormDataBodyData('multipart/form-data', { + someField: 'some value', + }); + + expect(expectedBody).toMatchInlineSnapshot(` + "--multipart/form-data + Content-Disposition: form-data; name="someField" + + some value + " + `); + expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length); + expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody); + }); + + it('should write when body is not empty and Content-Type includes multipart/form-data', () => { + const proxyRequest = fakeProxyRequest(); + proxyRequest.setHeader('content-type', 'multipart/form-data'); + + jest.spyOn(proxyRequest, 'setHeader'); + jest.spyOn(proxyRequest, 'write'); + + fixRequestBody(proxyRequest, createRequestWithBody({ someField: 'some value' })); + + const expectedBody = handlerFormDataBodyData('multipart/form-data', { + someField: 'some value', + }); + + expect(expectedBody).toMatchInlineSnapshot(` + "--multipart/form-data + Content-Disposition: form-data; name="someField" + + some value + " + `); + + expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length); + expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody); + }); + it('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');