From 6321b2c413bdb817f69eb111089b5d3afdb4eabd Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 29 Jul 2024 14:51:24 +0200 Subject: [PATCH] feat(NODE-5754): allow auto select family options --- .evergreen/run-typescript.sh | 0 src/cmap/connect.ts | 6 ++ src/connection_string.ts | 6 ++ src/mongo_client.ts | 2 +- .../node-specific/mongo_client.test.ts | 53 +++++++++++++++++ test/manual/mocharc.json | 5 +- test/manual/tls_support.test.ts | 59 +++++++++++++++++++ 7 files changed, 129 insertions(+), 2 deletions(-) mode change 100644 => 100755 .evergreen/run-typescript.sh diff --git a/.evergreen/run-typescript.sh b/.evergreen/run-typescript.sh old mode 100644 new mode 100755 diff --git a/src/cmap/connect.ts b/src/cmap/connect.ts index 58c3519fed..9a4ec80dc0 100644 --- a/src/cmap/connect.ts +++ b/src/cmap/connect.ts @@ -269,6 +269,8 @@ export const LEGAL_TLS_SOCKET_OPTIONS = [ /** @public */ export const LEGAL_TCP_SOCKET_OPTIONS = [ + 'autoSelectFamily', + 'autoSelectFamilyAttemptTimeout', 'family', 'hints', 'localAddress', @@ -287,6 +289,10 @@ function parseConnectOptions(options: ConnectionOptions): SocketConnectOpts { } } + if (!('autoSelectFamily' in result)) { + result.autoSelectFamily = true; + } + if (typeof hostAddress.socketPath === 'string') { result.path = hostAddress.socketPath; return result as net.IpcNetConnectOpts; diff --git a/src/connection_string.ts b/src/connection_string.ts index b0becafac0..37ecd5c567 100644 --- a/src/connection_string.ts +++ b/src/connection_string.ts @@ -740,6 +740,12 @@ export const OPTIONS = { autoEncryption: { type: 'record' }, + autoSelectFamily: { + type: 'boolean' + }, + autoSelectFamilyAttemptTimeout: { + type: 'uint' + }, bsonRegExp: { type: 'boolean' }, diff --git a/src/mongo_client.ts b/src/mongo_client.ts index aee241076f..b5cf49deb5 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -104,7 +104,7 @@ export type SupportedTLSSocketOptions = Pick< /** @public */ export type SupportedSocketOptions = Pick< - TcpNetConnectOpts, + TcpNetConnectOpts & { autoSelectFamily?: boolean; autoSelectFamilyAttemptTimeout?: number }, (typeof LEGAL_TCP_SOCKET_OPTIONS)[number] >; diff --git a/test/integration/node-specific/mongo_client.test.ts b/test/integration/node-specific/mongo_client.test.ts index b0653a702a..634516f8f7 100644 --- a/test/integration/node-specific/mongo_client.test.ts +++ b/test/integration/node-specific/mongo_client.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { once } from 'events'; +import * as net from 'net'; import * as sinon from 'sinon'; import { @@ -721,4 +722,56 @@ describe('class MongoClient', function () { }); }); }); + + context('when connecting', function () { + let netSpy; + + beforeEach(function () { + netSpy = sinon.spy(net, 'createConnection'); + }); + + afterEach(function () { + sinon.restore(); + }); + + context('when auto select options are provided', function () { + beforeEach(function () { + client = this.configuration.newClient({ + autoSelectFamily: false, + autoSelectFamilyAttemptTimeout: 100 + }); + }); + + it('sets the provided options', { + metadata: { requires: { topology: ['single'] } }, + test: async function () { + await client.connect(); + expect(netSpy).to.have.been.calledWith({ + autoSelectFamily: false, + autoSelectFamilyAttemptTimeout: 100, + host: 'localhost', + port: 27017 + }); + } + }); + }); + + context('when auto select options are not provided', function () { + beforeEach(function () { + client = this.configuration.newClient(); + }); + + it('sets the default options', { + metadata: { requires: { topology: ['single'] } }, + test: async function () { + await client.connect(); + expect(netSpy).to.have.been.calledWith({ + autoSelectFamily: true, + host: 'localhost', + port: 27017 + }); + } + }); + }); + }); }); diff --git a/test/manual/mocharc.json b/test/manual/mocharc.json index b52cf660c2..08a1a88d8e 100644 --- a/test/manual/mocharc.json +++ b/test/manual/mocharc.json @@ -1,5 +1,8 @@ { - "require": "ts-node/register", + "require": [ + "ts-node/register", + "test/tools/runner/chai_addons.ts" + ], "reporter": "test/tools/reporter/mongodb_reporter.js", "failZero": true, "color": true, diff --git a/test/manual/tls_support.test.ts b/test/manual/tls_support.test.ts index da89c71046..5359708626 100644 --- a/test/manual/tls_support.test.ts +++ b/test/manual/tls_support.test.ts @@ -1,7 +1,9 @@ import * as process from 'node:process'; +import * as tls from 'node:tls'; import { expect } from 'chai'; import { promises as fs } from 'fs'; +import * as sinon from 'sinon'; import { LEGACY_HELLO_COMMAND, @@ -61,6 +63,63 @@ describe('TLS Support', function () { }); context('when tls filepaths have length > 0', () => { + context('when auto family options are not set', function () { + let tlsSpy; + + afterEach(function () { + sinon.restore(); + }); + + beforeEach(function () { + client = new MongoClient(CONNECTION_STRING, tlsSettings); + tlsSpy = sinon.spy(tls, 'connect'); + }); + + it('sets the default options', async function () { + await client.connect(); + expect(tlsSpy).to.have.been.calledWith({ + autoSelectFamily: true, + host: 'localhost', + port: 27017, + servername: 'localhost', + ca: sinon.match.defined, + cert: sinon.match.defined, + key: sinon.match.defined + }); + }); + }); + + context('when auto family options are set', function () { + let tlsSpy; + + afterEach(function () { + sinon.restore(); + }); + + beforeEach(function () { + client = new MongoClient(CONNECTION_STRING, { + ...tlsSettings, + autoSelectFamily: false, + autoSelectFamilyAttemptTimeout: 100 + }); + tlsSpy = sinon.spy(tls, 'connect'); + }); + + it('sets the provided options', async function () { + await client.connect(); + expect(tlsSpy).to.have.been.calledWith({ + autoSelectFamily: false, + autoSelectFamilyAttemptTimeout: 100, + host: 'localhost', + port: 27017, + servername: 'localhost', + ca: sinon.match.defined, + cert: sinon.match.defined, + key: sinon.match.defined + }); + }); + }); + context('when connection will succeed', () => { beforeEach(async () => { client = new MongoClient(CONNECTION_STRING, tlsSettings);