From 4bd7f1cd726dff35f64b88a6dadeaa64130d18e0 Mon Sep 17 00:00:00 2001 From: Dan Aprahamian Date: Fri, 24 May 2019 13:53:15 -0400 Subject: [PATCH] fix(pool): clean up connections if pool is destroyed mid-handshake Fixes NODE-1971 --- lib/connection/pool.js | 1 + test/tests/unit/pool_tests.js | 61 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/lib/connection/pool.js b/lib/connection/pool.js index 6cd1a9e3c..f3fa9a7a2 100644 --- a/lib/connection/pool.js +++ b/lib/connection/pool.js @@ -561,6 +561,7 @@ Pool.prototype.connect = function() { } if (self.state === DESTROYED || self.state === DESTROYING) { + connection.destroy(); return self.destroy(); } diff --git a/test/tests/unit/pool_tests.js b/test/tests/unit/pool_tests.js index 5ff8171ed..2db6d1898 100644 --- a/test/tests/unit/pool_tests.js +++ b/test/tests/unit/pool_tests.js @@ -5,6 +5,7 @@ const mock = require('mongodb-mock-server'); const Server = require('../../../lib/topologies/server'); const MongoWriteConcernError = require('../../../lib/error').MongoWriteConcernError; const sinon = require('sinon'); +const Socket = require('net').Socket; const test = {}; describe('Pool (unit)', function() { @@ -77,4 +78,64 @@ describe('Pool (unit)', function() { client.connect(); }); + + it('should make sure to close connection if destroy is called mid-handshake', function(done) { + class Deferred { + constructor() { + this.promise = new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + }); + } + } + + function getActiveSockets() { + return new Set(process._getActiveHandles().filter(handle => handle instanceof Socket)); + } + + function diffSet(base, sub) { + const ret = new Set(); + for (const item of base) { + if (!sub.has(item)) { + ret.add(item); + } + } + + return ret; + } + + const requestReceived = new Deferred(); + const sendReply = new Deferred(); + + test.server.setMessageHandler(request => { + requestReceived.resolve(); + const doc = request.document; + if (doc.ismaster) { + sendReply.promise.then(() => { + request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + }); + } + }); + + const client = new Server(test.server.address()); + + const previouslyActiveSockets = getActiveSockets(); + client.connect(); + + requestReceived.promise.then(() => { + client.destroy({}, () => { + sendReply.resolve(); + setTimeout(() => { + const activeSockets = diffSet(getActiveSockets(), previouslyActiveSockets); + try { + expect(activeSockets.size).to.equal(0); + done(); + } catch (e) { + console.dir(activeSockets, { depth: 0 }); + done(e); + } + }, 50); + }); + }); + }); });