Skip to content

Commit 169eabb

Browse files
committed
http: fix http client leaky with double response
1 parent c6316f9 commit 169eabb

File tree

3 files changed

+55
-3
lines changed

3 files changed

+55
-3
lines changed

lib/_http_client.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,14 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
692692
// We already have a response object, this means the server
693693
// sent a double response.
694694
socket.destroy();
695-
return 0; // No special treatment.
695+
if (socket.parser) {
696+
// https://github.com/nodejs/node/issues/60025
697+
// Now, parser.incoming is pointed to the new IncomingMessage,
698+
// we need to rewrite it to the first one and skip all the pending IncomingMessage
699+
socket.parser.incoming = req.res;
700+
socket.parser.incoming.skipPendingData = true;
701+
}
702+
return 0;
696703
}
697704
req.res = res;
698705

lib/_http_common.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ function parserOnBody(b) {
126126
const stream = this.incoming;
127127

128128
// If the stream has already been removed, then drop it.
129-
if (stream === null)
129+
if (stream === null || stream.skipPendingData)
130130
return;
131131

132132
// Pretend this was the result of a stream._read call.
@@ -141,7 +141,7 @@ function parserOnMessageComplete() {
141141
const parser = this;
142142
const stream = parser.incoming;
143143

144-
if (stream !== null) {
144+
if (stream !== null && !stream.skipPendingData) {
145145
stream.complete = true;
146146
// Emit any trailing headers.
147147
const headers = parser._headers;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
// Flags: --expose-gc
3+
const common = require('../common');
4+
const http = require('http');
5+
const assert = require('assert');
6+
const { onGC } = require('../common/gc');
7+
8+
function createServer() {
9+
const server = http.createServer(common.mustCall((req, res) => {
10+
res.setHeader('Content-Type', 'application/json');
11+
res.end(JSON.stringify({ hello: 'world' }));
12+
req.socket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
13+
}));
14+
15+
return new Promise((resolve) => {
16+
server.listen(0, common.mustCall(() => {
17+
resolve(server);
18+
}));
19+
});
20+
}
21+
22+
async function main() {
23+
const server = await createServer();
24+
const req = http.get({
25+
host: '127.0.0.1',
26+
port: server.address().port,
27+
}, common.mustCall((res) => {
28+
const chunks = [];
29+
res.on('data', (c) => chunks.push(c));
30+
res.on('end', common.mustCall(() => {
31+
const body = Buffer.concat(chunks).toString('utf8');
32+
const data = JSON.parse(body);
33+
assert.strictEqual(data.hello, 'world');
34+
}));
35+
}));
36+
const timer = setInterval(global.gc, 500);
37+
onGC(req, {
38+
ongc: common.mustCall(() => {
39+
clearInterval(timer);
40+
server.close();
41+
})
42+
});
43+
}
44+
45+
main();

0 commit comments

Comments
 (0)