Skip to content

Commit 5d9f46f

Browse files
JS-markchimurai
andauthored
fix(fixRequestBody): support multipart/form-data (#896)
* fix(utils): fixRequestBody support `multipart/form-data` * fix(utils): add unit test and split func --------- Co-authored-by: Steven Chim <[email protected]>
1 parent 788a1fd commit 5d9f46f

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

src/handlers/fix-request-body.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,23 @@ export function fixRequestBody<TReq = http.IncomingMessage>(
3030
if (contentType && contentType.includes('application/x-www-form-urlencoded')) {
3131
writeBody(querystring.stringify(requestBody));
3232
}
33+
34+
if (contentType && contentType.includes('multipart/form-data')) {
35+
writeBody(handlerFormDataBodyData(contentType, requestBody));
36+
}
37+
}
38+
39+
/**
40+
* format FormData data
41+
* @param contentType
42+
* @param data
43+
* @returns
44+
*/
45+
function handlerFormDataBodyData(contentType: string, data: any) {
46+
const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1');
47+
let str = '';
48+
for (const [key, value] of Object.entries(data)) {
49+
str += `--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`;
50+
}
51+
return str;
3352
}

test/unit/fix-request-body.spec.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Socket } from 'net';
22
import { ClientRequest, IncomingMessage } from 'http';
33
import * as querystring from 'querystring';
4-
54
import { fixRequestBody, BodyParserLikeRequest } from '../../src/handlers/fix-request-body';
65

76
const fakeProxyRequest = (): ClientRequest => {
@@ -17,6 +16,15 @@ const createRequestWithBody = (body: unknown): BodyParserLikeRequest => {
1716
return req;
1817
};
1918

19+
const handlerFormDataBodyData = (contentType: string, data: { [key: string]: any }) => {
20+
const boundary = contentType.replace(/^.*boundary=(.*)$/, '$1');
21+
let str = '';
22+
for (const [key, value] of Object.entries(data)) {
23+
str += `--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`;
24+
}
25+
return str;
26+
};
27+
2028
describe('fixRequestBody', () => {
2129
it('should not write when body is undefined', () => {
2230
const proxyRequest = fakeProxyRequest();
@@ -57,6 +65,55 @@ describe('fixRequestBody', () => {
5765
expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody);
5866
});
5967

68+
it('should write when body is not empty and Content-Type is multipart/form-data', () => {
69+
const proxyRequest = fakeProxyRequest();
70+
proxyRequest.setHeader('content-type', 'multipart/form-data');
71+
72+
jest.spyOn(proxyRequest, 'setHeader');
73+
jest.spyOn(proxyRequest, 'write');
74+
75+
fixRequestBody(proxyRequest, createRequestWithBody({ someField: 'some value' }));
76+
77+
const expectedBody = handlerFormDataBodyData('multipart/form-data', {
78+
someField: 'some value',
79+
});
80+
81+
expect(expectedBody).toMatchInlineSnapshot(`
82+
"--multipart/form-data
83+
Content-Disposition: form-data; name="someField"
84+
85+
some value
86+
"
87+
`);
88+
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
89+
expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody);
90+
});
91+
92+
it('should write when body is not empty and Content-Type includes multipart/form-data', () => {
93+
const proxyRequest = fakeProxyRequest();
94+
proxyRequest.setHeader('content-type', 'multipart/form-data');
95+
96+
jest.spyOn(proxyRequest, 'setHeader');
97+
jest.spyOn(proxyRequest, 'write');
98+
99+
fixRequestBody(proxyRequest, createRequestWithBody({ someField: 'some value' }));
100+
101+
const expectedBody = handlerFormDataBodyData('multipart/form-data', {
102+
someField: 'some value',
103+
});
104+
105+
expect(expectedBody).toMatchInlineSnapshot(`
106+
"--multipart/form-data
107+
Content-Disposition: form-data; name="someField"
108+
109+
some value
110+
"
111+
`);
112+
113+
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
114+
expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody);
115+
});
116+
60117
it('should write when body is not empty and Content-Type ends with +json', () => {
61118
const proxyRequest = fakeProxyRequest();
62119
proxyRequest.setHeader('content-type', 'application/merge-patch+json; charset=utf-8');
@@ -65,7 +122,6 @@ describe('fixRequestBody', () => {
65122
jest.spyOn(proxyRequest, 'write');
66123

67124
fixRequestBody(proxyRequest, createRequestWithBody({ someField: 'some value' }));
68-
69125
const expectedBody = JSON.stringify({ someField: 'some value' });
70126
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
71127
expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody);

0 commit comments

Comments
 (0)