Skip to content

Commit 7ba87a9

Browse files
authored
feat(debug-proxy-errors-plugin): debug message for bodyParser + ECONNRESET error (#1273)
1 parent 006f1a4 commit 7ba87a9

2 files changed

Lines changed: 47 additions & 0 deletions

File tree

src/plugins/default/debug-proxy-errors-plugin.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
1+
import type { IncomingMessage } from 'node:http';
2+
import { styleText } from 'node:util';
3+
14
import { Debug } from '../../debug.js';
25
import type { Plugin } from '../../types.js';
36
import { definePlugin } from '../define-plugin.js';
47

58
const debug = Debug.extend('debug-proxy-errors-plugin');
69

10+
const BODY_PARSER_ERROR_MESSAGE = `[HPM] Connection reset (ECONNRESET) detected with non-empty "req.body [ERR_HPM.GH40]".
11+
12+
This usually means that the POST request body (req.body) was already parsed before reaching the proxy.
13+
When bodyParser runs first, it consumes the request stream, leaving the proxy unable to forward the body data to the target server.
14+
15+
How to fix this issue:
16+
- Option 1: Place the proxy middleware before the bodyParser middleware.
17+
- Option 2: Use 'fixRequestBody()' helper to fix this issue.
18+
19+
For more details, see: https://github.com/chimurai/http-proxy-middleware/issues/40\n`;
20+
21+
function hasParsedBody(req: IncomingMessage | undefined): boolean {
22+
return Boolean(req && req.method === 'POST' && 'body' in req && req.body);
23+
}
24+
725
/**
826
* Subscribe to {@link https://github.com/unjs/httpxy#events `httpxy` error events} to prevent server from crashing.
927
* Errors are logged with {@link https://www.npmjs.com/package/debug debug} library.
@@ -16,6 +34,11 @@ export const debugProxyErrorsPlugin: Plugin = definePlugin((proxyServer, options
1634
*/
1735
proxyServer.on('error', (error, req, res, target) => {
1836
debug(`httpxy error event: \n%O`, error);
37+
38+
// detect request body (when bodyParser used) and log an error message to help debugging
39+
if ((error as any).code === 'ECONNRESET' && hasParsedBody(req)) {
40+
console.error(styleText('red', BODY_PARSER_ERROR_MESSAGE));
41+
}
1942
});
2043

2144
proxyServer.on('proxyReq', (proxyReq, req, socket) => {

test/e2e/http-proxy-middleware.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,30 @@ describe('E2E http-proxy-middleware', () => {
131131
await agent.post('/api').send({ foo: 'bar', bar: 'baz', doubleByte: '文' }).expect(200);
132132
});
133133

134+
it('should detect bodyParser usage leading to ECONNRESET error', async () => {
135+
const logSpy = vi.spyOn(console, 'error');
136+
137+
agent = request(
138+
createApp(
139+
bodyParser.json(),
140+
createProxyMiddleware({
141+
target: mockTargetServer.url,
142+
pathFilter: '/api',
143+
}),
144+
),
145+
);
146+
147+
try {
148+
await mockTargetServer.forPost('/api').thenResetConnection();
149+
await agent.post('/api').send({ foo: 'bar', bar: 'baz', doubleByte: '文' }).expect(504);
150+
151+
expect(logSpy).toHaveBeenCalledTimes(1);
152+
expect(logSpy.mock.calls[0][0]).toContain('[ERR_HPM.GH40]');
153+
} finally {
154+
logSpy.mockRestore();
155+
}
156+
});
157+
134158
it('should reject CRLF multipart injection when fixRequestBody serializes multipart', async () => {
135159
const targetSpy = vi.fn();
136160
const loggerSpy: Logger = {

0 commit comments

Comments
 (0)