Skip to content

Commit

Permalink
http: detach socket from IncomingMessage on keep-alive
Browse files Browse the repository at this point in the history
If the socket is not detached then a future call to res.destroy
(through e.g. pipeline) would unecessarily kill the socket while
its in the agent free list.

PR-URL: #32153
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
ronag committed Mar 10, 2020
1 parent 30bbeb7 commit c1b2f6a
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 1 deletion.
6 changes: 6 additions & 0 deletions lib/_http_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,12 @@ function responseKeepAlive(req) {
// Mark this socket as available, AFTER user-added end
// handlers have a chance to run.
defaultTriggerAsyncIdScope(asyncId, process.nextTick, emitFreeNT, req);

if (req.res) {
// Detach socket from IncomingMessage to avoid destroying the freed
// socket in IncomingMessage.destroy().
req.res.socket = null;
}
}

function responseOnEnd() {
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-http-agent-destroyed-socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const server = http.createServer(common.mustCall((req, res) => {
response.on('end', common.mustCall(() => {
request1.socket.destroy();

response.socket.once('close', common.mustCall(() => {
request1.socket.once('close', common.mustCall(() => {
// Assert request2 was removed from the queue
assert(!agent.requests[key]);
process.nextTick(() => {
Expand Down
39 changes: 39 additions & 0 deletions test/parallel/test-http-client-abort-keep-alive-destroy-res.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');

let socketsCreated = 0;

class Agent extends http.Agent {
createConnection(options, oncreate) {
const socket = super.createConnection(options, oncreate);
socketsCreated++;
return socket;
}
}

const server = http.createServer((req, res) => res.end());

server.listen(0, common.mustCall(() => {
const port = server.address().port;
const agent = new Agent({
keepAlive: true,
maxSockets: 1
});

const req = http.get({ agent, port }, common.mustCall((res) => {
res.resume();
res.on('end', () => {
res.destroy();

http.get({ agent, port }, common.mustCall((res) => {
res.resume();
assert.strictEqual(socketsCreated, 1);
agent.destroy();
server.close();
}));
});
}));
req.end();
}));

0 comments on commit c1b2f6a

Please sign in to comment.