Skip to content

Commit d22d587

Browse files
authored
fix(fixRequestBody): check readableLength (#1097)
1 parent d03d51b commit d22d587

File tree

2 files changed

+30
-91
lines changed

2 files changed

+30
-91
lines changed

src/handlers/fix-request-body.ts

+6-34
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,15 @@ import type * as http from 'http';
22
import type { Request } from '../types';
33
import * as querystring from 'querystring';
44

5-
type HandleBadRequestArgs = {
6-
proxyReq: http.ClientRequest;
7-
req: http.IncomingMessage;
8-
res: http.ServerResponse;
9-
};
10-
115
/**
126
* Fix proxied body if bodyParser is involved.
137
*/
14-
export function fixRequestBody(
15-
proxyReq: http.ClientRequest,
16-
req: http.IncomingMessage,
17-
res: http.ServerResponse
18-
): void {
8+
export function fixRequestBody(proxyReq: http.ClientRequest, req: http.IncomingMessage): void {
9+
// skip fixRequestBody() when req.readableLength not 0 (bodyParser failure)
10+
if (req.readableLength !== 0) {
11+
return;
12+
}
13+
1914
const requestBody = (req as Request).body;
2015

2116
if (!requestBody) {
@@ -28,18 +23,6 @@ export function fixRequestBody(
2823
return;
2924
}
3025

31-
// Handle bad request when unexpected "Connect: Upgrade" header is provided
32-
if (/upgrade/gi.test(proxyReq.getHeader('Connection') as string)) {
33-
handleBadRequest({ proxyReq, req, res });
34-
return;
35-
}
36-
37-
// Handle bad request when invalid request body is provided
38-
if (hasInvalidKeys(requestBody)) {
39-
handleBadRequest({ proxyReq, req, res });
40-
return;
41-
}
42-
4326
const writeBody = (bodyData: string) => {
4427
// deepcode ignore ContentLengthInCode: bodyParser fix
4528
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
@@ -52,14 +35,3 @@ export function fixRequestBody(
5235
writeBody(querystring.stringify(requestBody));
5336
}
5437
}
55-
56-
function hasInvalidKeys(obj) {
57-
return Object.keys(obj).some((key) => /[\n\r]/.test(key));
58-
}
59-
60-
function handleBadRequest({ proxyReq, req, res }: HandleBadRequestArgs) {
61-
res.writeHead(400);
62-
res.end('Bad Request');
63-
proxyReq.destroy();
64-
req.destroy();
65-
}

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

+24-57
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,21 @@ const fakeProxyResponse = (): ServerResponse => {
1717
return res;
1818
};
1919

20+
const createRequestWithBody = (body: unknown): Request => {
21+
const req = new IncomingMessage(new Socket()) as Request;
22+
req.url = '/test_path';
23+
req.body = body;
24+
return req;
25+
};
26+
2027
describe('fixRequestBody', () => {
2128
it('should not write when body is undefined', () => {
2229
const proxyRequest = fakeProxyRequest();
2330

2431
jest.spyOn(proxyRequest, 'setHeader');
2532
jest.spyOn(proxyRequest, 'write');
2633

27-
fixRequestBody(proxyRequest, { body: undefined } as Request, fakeProxyResponse());
34+
fixRequestBody(proxyRequest, createRequestWithBody(undefined));
2835

2936
expect(proxyRequest.setHeader).not.toHaveBeenCalled();
3037
expect(proxyRequest.write).not.toHaveBeenCalled();
@@ -37,7 +44,7 @@ describe('fixRequestBody', () => {
3744
jest.spyOn(proxyRequest, 'setHeader');
3845
jest.spyOn(proxyRequest, 'write');
3946

40-
fixRequestBody(proxyRequest, { body: {} } as Request, fakeProxyResponse());
47+
fixRequestBody(proxyRequest, createRequestWithBody({}));
4148

4249
expect(proxyRequest.setHeader).toHaveBeenCalled();
4350
expect(proxyRequest.write).toHaveBeenCalled();
@@ -50,11 +57,7 @@ describe('fixRequestBody', () => {
5057
jest.spyOn(proxyRequest, 'setHeader');
5158
jest.spyOn(proxyRequest, 'write');
5259

53-
fixRequestBody(
54-
proxyRequest,
55-
{ body: { someField: 'some value' } } as Request,
56-
fakeProxyResponse()
57-
);
60+
fixRequestBody(proxyRequest, createRequestWithBody({ someField: 'some value' }));
5861

5962
const expectedBody = JSON.stringify({ someField: 'some value' });
6063
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
@@ -68,11 +71,7 @@ describe('fixRequestBody', () => {
6871
jest.spyOn(proxyRequest, 'setHeader');
6972
jest.spyOn(proxyRequest, 'write');
7073

71-
fixRequestBody(
72-
proxyRequest,
73-
{ body: { someField: 'some value' } } as Request,
74-
fakeProxyResponse()
75-
);
74+
fixRequestBody(proxyRequest, createRequestWithBody({ someField: 'some value' }));
7675

7776
const expectedBody = querystring.stringify({ someField: 'some value' });
7877
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
@@ -86,11 +85,7 @@ describe('fixRequestBody', () => {
8685
jest.spyOn(proxyRequest, 'setHeader');
8786
jest.spyOn(proxyRequest, 'write');
8887

89-
fixRequestBody(
90-
proxyRequest,
91-
{ body: { someField: 'some value' } } as Request,
92-
fakeProxyResponse()
93-
);
88+
fixRequestBody(proxyRequest, createRequestWithBody({ someField: 'some value' }));
9489

9590
const expectedBody = querystring.stringify({ someField: 'some value' });
9691
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
@@ -104,62 +99,34 @@ describe('fixRequestBody', () => {
10499
jest.spyOn(proxyRequest, 'setHeader');
105100
jest.spyOn(proxyRequest, 'write');
106101

107-
fixRequestBody(
108-
proxyRequest,
109-
{ body: { someField: 'some value' } } as Request,
110-
fakeProxyResponse()
111-
);
102+
fixRequestBody(proxyRequest, createRequestWithBody({ someField: 'some value' }));
112103

113104
const expectedBody = JSON.stringify({ someField: 'some value' });
114105
expect(proxyRequest.setHeader).toHaveBeenCalledWith('Content-Length', expectedBody.length);
115106
expect(proxyRequest.write).toHaveBeenCalledTimes(1);
116107
expect(proxyRequest.write).toHaveBeenCalledWith(expectedBody);
117108
});
118109

119-
it('should return 400 and abort request on "Connection: Upgrade" header', () => {
110+
it('should not fixRequestBody() when there bodyParser fails', () => {
120111
const proxyRequest = fakeProxyRequest();
121-
const request = { body: { someField: 'some value' } } as Request;
122-
123-
proxyRequest.destroy = jest.fn();
124-
request.destroy = jest.fn();
125-
126-
const proxyResponse = fakeProxyResponse();
127-
proxyRequest.setHeader('connection', 'upgrade');
128-
proxyRequest.setHeader('content-type', 'application/x-www-form-urlencoded');
129-
130-
jest.spyOn(proxyRequest, 'destroy');
131-
jest.spyOn(request, 'destroy');
132-
jest.spyOn(proxyResponse, 'writeHead');
133-
jest.spyOn(proxyResponse, 'end');
134-
135-
fixRequestBody(proxyRequest, request, proxyResponse);
136-
137-
expect(proxyResponse.writeHead).toHaveBeenCalledWith(400);
138-
expect(proxyResponse.end).toHaveBeenCalledTimes(1);
139-
expect(proxyRequest.destroy).toHaveBeenCalledTimes(1);
140-
expect(request.destroy).toHaveBeenCalledTimes(1);
141-
});
142-
143-
it('should return 400 and abort request on invalid request data', () => {
144-
const proxyRequest = fakeProxyRequest();
145-
const request = { body: { 'INVALID \n\r DATA': '' } } as Request;
146-
147-
proxyRequest.destroy = jest.fn();
148-
request.destroy = jest.fn();
112+
const request = {
113+
get readableLength() {
114+
return 4444; // simulate bodyParser failure
115+
},
116+
} as Request;
149117

150118
const proxyResponse = fakeProxyResponse();
151119
proxyRequest.setHeader('content-type', 'application/x-www-form-urlencoded');
152120

121+
jest.spyOn(proxyRequest, 'write');
153122
jest.spyOn(proxyRequest, 'destroy');
154-
jest.spyOn(request, 'destroy');
155123
jest.spyOn(proxyResponse, 'writeHead');
156124
jest.spyOn(proxyResponse, 'end');
157125

158-
fixRequestBody(proxyRequest, request, proxyResponse);
126+
fixRequestBody(proxyRequest, request);
159127

160-
expect(proxyResponse.writeHead).toHaveBeenCalledWith(400);
161-
expect(proxyResponse.end).toHaveBeenCalledTimes(1);
162-
expect(proxyRequest.destroy).toHaveBeenCalledTimes(1);
163-
expect(request.destroy).toHaveBeenCalledTimes(1);
128+
expect(proxyResponse.end).toHaveBeenCalledTimes(0);
129+
expect(proxyRequest.write).toHaveBeenCalledTimes(0);
130+
expect(proxyRequest.destroy).toHaveBeenCalledTimes(0);
164131
});
165132
});

0 commit comments

Comments
 (0)