mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
http: fix http client leaky with double response
PR-URL: https://github.com/nodejs/node/pull/60062 Fixes: https://github.com/nodejs/node/issues/60025 Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
parent
8bc7dfd16f
commit
59b70e5fe3
|
|
@ -47,6 +47,7 @@ const {
|
||||||
HTTPParser,
|
HTTPParser,
|
||||||
isLenient,
|
isLenient,
|
||||||
prepareError,
|
prepareError,
|
||||||
|
kSkipPendingData,
|
||||||
} = require('_http_common');
|
} = require('_http_common');
|
||||||
const {
|
const {
|
||||||
kUniqueHeaders,
|
kUniqueHeaders,
|
||||||
|
|
@ -692,7 +693,14 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
|
||||||
// We already have a response object, this means the server
|
// We already have a response object, this means the server
|
||||||
// sent a double response.
|
// sent a double response.
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
return 0; // No special treatment.
|
if (socket.parser) {
|
||||||
|
// https://github.com/nodejs/node/issues/60025
|
||||||
|
// Now, parser.incoming is pointed to the new IncomingMessage,
|
||||||
|
// we need to rewrite it to the first one and skip all the pending IncomingMessage
|
||||||
|
socket.parser.incoming = req.res;
|
||||||
|
socket.parser.incoming[kSkipPendingData] = true;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
req.res = res;
|
req.res = res;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ const {
|
||||||
} = incoming;
|
} = incoming;
|
||||||
|
|
||||||
const kIncomingMessage = Symbol('IncomingMessage');
|
const kIncomingMessage = Symbol('IncomingMessage');
|
||||||
|
const kSkipPendingData = Symbol('SkipPendingData');
|
||||||
const kOnMessageBegin = HTTPParser.kOnMessageBegin | 0;
|
const kOnMessageBegin = HTTPParser.kOnMessageBegin | 0;
|
||||||
const kOnHeaders = HTTPParser.kOnHeaders | 0;
|
const kOnHeaders = HTTPParser.kOnHeaders | 0;
|
||||||
const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
|
const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
|
||||||
|
|
@ -126,7 +127,7 @@ function parserOnBody(b) {
|
||||||
const stream = this.incoming;
|
const stream = this.incoming;
|
||||||
|
|
||||||
// If the stream has already been removed, then drop it.
|
// If the stream has already been removed, then drop it.
|
||||||
if (stream === null)
|
if (stream === null || stream[kSkipPendingData])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Pretend this was the result of a stream._read call.
|
// Pretend this was the result of a stream._read call.
|
||||||
|
|
@ -141,7 +142,7 @@ function parserOnMessageComplete() {
|
||||||
const parser = this;
|
const parser = this;
|
||||||
const stream = parser.incoming;
|
const stream = parser.incoming;
|
||||||
|
|
||||||
if (stream !== null) {
|
if (stream !== null && !stream[kSkipPendingData]) {
|
||||||
stream.complete = true;
|
stream.complete = true;
|
||||||
// Emit any trailing headers.
|
// Emit any trailing headers.
|
||||||
const headers = parser._headers;
|
const headers = parser._headers;
|
||||||
|
|
@ -310,4 +311,5 @@ module.exports = {
|
||||||
HTTPParser,
|
HTTPParser,
|
||||||
isLenient,
|
isLenient,
|
||||||
prepareError,
|
prepareError,
|
||||||
|
kSkipPendingData,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
44
test/parallel/test-http-client-leaky-with-double-response.js
Normal file
44
test/parallel/test-http-client-leaky-with-double-response.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
'use strict';
|
||||||
|
// Flags: --expose-gc
|
||||||
|
const common = require('../common');
|
||||||
|
const http = require('http');
|
||||||
|
const assert = require('assert');
|
||||||
|
const { onGC } = require('../common/gc');
|
||||||
|
|
||||||
|
function createServer() {
|
||||||
|
const server = http.createServer(common.mustCall((req, res) => {
|
||||||
|
res.setHeader('Content-Type', 'application/json');
|
||||||
|
res.end(JSON.stringify({ hello: 'world' }));
|
||||||
|
req.socket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
|
||||||
|
}));
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
server.listen(0, common.mustCall(() => {
|
||||||
|
resolve(server);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const server = await createServer();
|
||||||
|
const req = http.get({
|
||||||
|
port: server.address().port,
|
||||||
|
}, common.mustCall((res) => {
|
||||||
|
const chunks = [];
|
||||||
|
res.on('data', common.mustCallAtLeast((c) => chunks.push(c), 1));
|
||||||
|
res.on('end', common.mustCall(() => {
|
||||||
|
const body = Buffer.concat(chunks).toString('utf8');
|
||||||
|
const data = JSON.parse(body);
|
||||||
|
assert.strictEqual(data.hello, 'world');
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
const timer = setInterval(global.gc, 300);
|
||||||
|
onGC(req, {
|
||||||
|
ongc: common.mustCall(() => {
|
||||||
|
clearInterval(timer);
|
||||||
|
server.close();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
Loading…
Reference in New Issue
Block a user