From b26c32800ecb2658a0d9c779bdb741aa06a629d0 Mon Sep 17 00:00:00 2001 From: Aditi Khare <106987683+aditi-khare-mongoDB@users.noreply.github.com> Date: Tue, 30 Jul 2024 13:30:23 -0400 Subject: [PATCH 1/2] fix(NODE-5720): on pre-4.4 sharded servers, the node driver uses `error.writeConcern.code` to determine retryability (#4155) --- src/cmap/connect.ts | 2 +- src/error.ts | 19 +++++++++++++++---- src/sdam/server.ts | 2 +- .../retryable_writes.spec.test.ts | 3 --- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/cmap/connect.ts b/src/cmap/connect.ts index e319dbbed9..58c3519fed 100644 --- a/src/cmap/connect.ts +++ b/src/cmap/connect.ts @@ -164,7 +164,7 @@ export async function performInitialHandshake( } catch (error) { if (error instanceof MongoError) { error.addErrorLabel(MongoErrorLabel.HandshakeError); - if (needsRetryableWriteLabel(error, response.maxWireVersion)) { + if (needsRetryableWriteLabel(error, response.maxWireVersion, conn.description.type)) { error.addErrorLabel(MongoErrorLabel.RetryableWriteError); } } diff --git a/src/error.ts b/src/error.ts index 8ef6c82f55..5ce02c82ab 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,4 +1,5 @@ import type { Document } from './bson'; +import type { ServerType } from './sdam/common'; import type { TopologyVersion } from './sdam/server_description'; import type { TopologyDescription } from './sdam/topology_description'; @@ -1226,7 +1227,11 @@ const RETRYABLE_READ_ERROR_CODES = new Set([ // see: https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst#terms const RETRYABLE_WRITE_ERROR_CODES = RETRYABLE_READ_ERROR_CODES; -export function needsRetryableWriteLabel(error: Error, maxWireVersion: number): boolean { +export function needsRetryableWriteLabel( + error: Error, + maxWireVersion: number, + serverType: ServerType +): boolean { // pre-4.4 server, then the driver adds an error label for every valid case // execute operation will only inspect the label, code/message logic is handled here if (error instanceof MongoNetworkError) { @@ -1246,11 +1251,17 @@ export function needsRetryableWriteLabel(error: Error, maxWireVersion: number): } if (error instanceof MongoWriteConcernError) { - return RETRYABLE_WRITE_ERROR_CODES.has(error.result.writeConcernError.code ?? error?.code ?? 0); + if (serverType === 'Mongos' && maxWireVersion < 9) { + // use original top-level code from server response + return RETRYABLE_WRITE_ERROR_CODES.has(error.result.code ?? 0); + } + return RETRYABLE_WRITE_ERROR_CODES.has( + error.result.writeConcernError.code ?? Number(error.code) ?? 0 + ); } - if (error instanceof MongoError && typeof error.code === 'number') { - return RETRYABLE_WRITE_ERROR_CODES.has(error.code); + if (error instanceof MongoError) { + return RETRYABLE_WRITE_ERROR_CODES.has(Number(error.code)); } const isNotWritablePrimaryError = LEGACY_NOT_WRITABLE_PRIMARY_ERROR_MESSAGE.test(error.message); diff --git a/src/sdam/server.ts b/src/sdam/server.ts index 59a7231b4f..b4450f0072 100644 --- a/src/sdam/server.ts +++ b/src/sdam/server.ts @@ -453,7 +453,7 @@ export class Server extends TypedEventEmitter { } else { if ( (isRetryableWritesEnabled(this.topology) || isTransactionCommand(cmd)) && - needsRetryableWriteLabel(error, maxWireVersion(this)) && + needsRetryableWriteLabel(error, maxWireVersion(this), this.description.type) && !inActiveTransaction(session, cmd) ) { error.addErrorLabel(MongoErrorLabel.RetryableWriteError); diff --git a/test/integration/retryable-writes/retryable_writes.spec.test.ts b/test/integration/retryable-writes/retryable_writes.spec.test.ts index 29b639e124..1c9e510e4f 100644 --- a/test/integration/retryable-writes/retryable_writes.spec.test.ts +++ b/test/integration/retryable-writes/retryable_writes.spec.test.ts @@ -19,9 +19,6 @@ describe('Retryable Writes (unified)', function () { runUnifiedSuite(loadSpecTests(path.join('retryable-writes', 'unified')), ({ description }) => { return clientBulkWriteTests.includes(description) ? `TODO(NODE-6257): implement client-level bulk write.` - : description === - 'RetryableWriteError label is not added based on writeConcernError in pre-4.4 mongos response' - ? 'TODO(NODE-5720)' : false; }); }); From 54efb7d497f8efb9953f5d315267e299ed4cf5af Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 1 Aug 2024 21:20:02 +0200 Subject: [PATCH 2/2] feat(NODE-5754): allow auto select family options (#4185) --- .evergreen/run-typescript.sh | 0 src/client-side-encryption/auto_encrypter.ts | 20 ++++++- .../client_encryption.ts | 38 ++++++++++-- src/client-side-encryption/state_machine.ts | 34 +++++++++-- src/cmap/connect.ts | 2 + src/connection_string.ts | 7 +++ src/index.ts | 1 + src/mongo_client.ts | 2 +- .../node-specific/mongo_client.test.ts | 53 +++++++++++++++++ test/manual/mocharc.json | 5 +- test/manual/tls_support.test.ts | 59 +++++++++++++++++++ .../auto_encrypter.test.ts | 21 +++++-- .../client_encryption.test.ts | 5 ++ .../state_machine.test.ts | 24 ++++++++ 14 files changed, 249 insertions(+), 22 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/client-side-encryption/auto_encrypter.ts b/src/client-side-encryption/auto_encrypter.ts index 3066b8d1f0..5ac3945f5e 100644 --- a/src/client-side-encryption/auto_encrypter.ts +++ b/src/client-side-encryption/auto_encrypter.ts @@ -3,6 +3,7 @@ import { type MongoCryptConstructor, type MongoCryptOptions } from 'mongodb-client-encryption'; +import * as net from 'net'; import { deserialize, type Document, serialize } from '../bson'; import { type CommandOptions, type ProxyOptions } from '../cmap/connection'; @@ -11,6 +12,7 @@ import { getMongoDBClientEncryption } from '../deps'; import { MongoRuntimeError } from '../error'; import { MongoClient, type MongoClientOptions } from '../mongo_client'; import { MongoDBCollectionNamespace } from '../utils'; +import { autoSelectSocketOptions } from './client_encryption'; import * as cryptoCallbacks from './crypto_callbacks'; import { MongoCryptInvalidArgumentError } from './errors'; import { MongocryptdManager } from './mongocryptd_manager'; @@ -297,10 +299,20 @@ export class AutoEncrypter { serverSelectionTimeoutMS: 10000 }; - if (options.extraOptions == null || typeof options.extraOptions.mongocryptdURI !== 'string') { + if ( + (options.extraOptions == null || typeof options.extraOptions.mongocryptdURI !== 'string') && + !net.getDefaultAutoSelectFamily + ) { + // Only set family if autoSelectFamily options are not supported. clientOptions.family = 4; } + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore: TS complains as this always returns true on versions where it is present. + if (net.getDefaultAutoSelectFamily) { + Object.assign(clientOptions, autoSelectSocketOptions(this._client.options)); + } + this._mongocryptdClient = new MongoClient(this._mongocryptdManager.uri, clientOptions); } } @@ -379,7 +391,8 @@ export class AutoEncrypter { promoteValues: false, promoteLongs: false, proxyOptions: this._proxyOptions, - tlsOptions: this._tlsOptions + tlsOptions: this._tlsOptions, + socketOptions: autoSelectSocketOptions(this._client.options) }); return deserialize(await stateMachine.execute(this, context), { @@ -399,7 +412,8 @@ export class AutoEncrypter { const stateMachine = new StateMachine({ ...options, proxyOptions: this._proxyOptions, - tlsOptions: this._tlsOptions + tlsOptions: this._tlsOptions, + socketOptions: autoSelectSocketOptions(this._client.options) }); return await stateMachine.execute(this, context); diff --git a/src/client-side-encryption/client_encryption.ts b/src/client-side-encryption/client_encryption.ts index b78b354996..03d2ff2de2 100644 --- a/src/client-side-encryption/client_encryption.ts +++ b/src/client-side-encryption/client_encryption.ts @@ -12,7 +12,7 @@ import { type Collection } from '../collection'; import { type FindCursor } from '../cursor/find_cursor'; import { type Db } from '../db'; import { getMongoDBClientEncryption } from '../deps'; -import { type MongoClient } from '../mongo_client'; +import { type MongoClient, type MongoClientOptions } from '../mongo_client'; import { type Filter, type WithId } from '../mongo_types'; import { type CreateCollectionOptions } from '../operations/create_collection'; import { type DeleteResult } from '../operations/delete'; @@ -28,7 +28,11 @@ import { type KMSProviders, refreshKMSCredentials } from './providers/index'; -import { type CSFLEKMSTlsOptions, StateMachine } from './state_machine'; +import { + type ClientEncryptionSocketOptions, + type CSFLEKMSTlsOptions, + StateMachine +} from './state_machine'; /** * @public @@ -199,7 +203,8 @@ export class ClientEncryption { const stateMachine = new StateMachine({ proxyOptions: this._proxyOptions, - tlsOptions: this._tlsOptions + tlsOptions: this._tlsOptions, + socketOptions: autoSelectSocketOptions(this._client.options) }); const dataKey = deserialize(await stateMachine.execute(this, context)) as DataKey; @@ -256,7 +261,8 @@ export class ClientEncryption { const context = this._mongoCrypt.makeRewrapManyDataKeyContext(filterBson, keyEncryptionKeyBson); const stateMachine = new StateMachine({ proxyOptions: this._proxyOptions, - tlsOptions: this._tlsOptions + tlsOptions: this._tlsOptions, + socketOptions: autoSelectSocketOptions(this._client.options) }); const { v: dataKeys } = deserialize(await stateMachine.execute(this, context)); @@ -637,7 +643,8 @@ export class ClientEncryption { const stateMachine = new StateMachine({ proxyOptions: this._proxyOptions, - tlsOptions: this._tlsOptions + tlsOptions: this._tlsOptions, + socketOptions: autoSelectSocketOptions(this._client.options) }); const { v } = deserialize(await stateMachine.execute(this, context)); @@ -715,7 +722,8 @@ export class ClientEncryption { const valueBuffer = serialize({ v: value }); const stateMachine = new StateMachine({ proxyOptions: this._proxyOptions, - tlsOptions: this._tlsOptions + tlsOptions: this._tlsOptions, + socketOptions: autoSelectSocketOptions(this._client.options) }); const context = this._mongoCrypt.makeExplicitEncryptionContext(valueBuffer, contextOptions); @@ -957,3 +965,21 @@ export interface RangeOptions { sparsity: Long; precision?: number; } + +/** + * Get the socket options from the client. + * @param baseOptions - The mongo client options. + * @returns ClientEncryptionSocketOptions + */ +export function autoSelectSocketOptions( + baseOptions: MongoClientOptions +): ClientEncryptionSocketOptions { + const options: ClientEncryptionSocketOptions = { autoSelectFamily: true }; + if ('autoSelectFamily' in baseOptions) { + options.autoSelectFamily = baseOptions.autoSelectFamily; + } + if ('autoSelectFamilyAttemptTimeout' in baseOptions) { + options.autoSelectFamilyAttemptTimeout = baseOptions.autoSelectFamilyAttemptTimeout; + } + return options; +} diff --git a/src/client-side-encryption/state_machine.ts b/src/client-side-encryption/state_machine.ts index f0ae19546a..af3ea4c215 100644 --- a/src/client-side-encryption/state_machine.ts +++ b/src/client-side-encryption/state_machine.ts @@ -14,7 +14,7 @@ import { type ProxyOptions } from '../cmap/connection'; import { getSocks, type SocksLib } from '../deps'; import { type MongoClient, type MongoClientOptions } from '../mongo_client'; import { BufferPool, MongoDBCollectionNamespace, promiseWithResolvers } from '../utils'; -import { type DataKey } from './client_encryption'; +import { autoSelectSocketOptions, type DataKey } from './client_encryption'; import { MongoCryptError } from './errors'; import { type MongocryptdManager } from './mongocryptd_manager'; import { type KMSProviders } from './providers'; @@ -114,6 +114,16 @@ export type CSFLEKMSTlsOptions = { [key: string]: ClientEncryptionTlsOptions | undefined; }; +/** + * @public + * + * Socket options to use for KMS requests. + */ +export type ClientEncryptionSocketOptions = Pick< + MongoClientOptions, + 'autoSelectFamily' | 'autoSelectFamilyAttemptTimeout' +>; + /** * This is kind of a hack. For `rewrapManyDataKey`, we have tests that * guarantee that when there are no matching keys, `rewrapManyDataKey` returns @@ -153,6 +163,9 @@ export type StateMachineOptions = { /** TLS options for KMS requests, if set. */ tlsOptions: CSFLEKMSTlsOptions; + + /** Socket specific options we support. */ + socketOptions: ClientEncryptionSocketOptions; } & Pick; /** @@ -289,10 +302,17 @@ export class StateMachine { async kmsRequest(request: MongoCryptKMSRequest): Promise { const parsedUrl = request.endpoint.split(':'); const port = parsedUrl[1] != null ? Number.parseInt(parsedUrl[1], 10) : HTTPS_PORT; - const options: tls.ConnectionOptions & { host: string; port: number } = { + const socketOptions = autoSelectSocketOptions(this.options.socketOptions || {}); + const options: tls.ConnectionOptions & { + host: string; + port: number; + autoSelectFamily?: boolean; + autoSelectFamilyAttemptTimeout?: number; + } = { host: parsedUrl[0], servername: parsedUrl[0], - port + port, + ...socketOptions }; const message = request.message; const buffer = new BufferPool(); @@ -351,10 +371,12 @@ export class StateMachine { try { if (this.options.proxyOptions && this.options.proxyOptions.proxyHost) { - netSocket.connect({ + const netSocketOptions = { host: this.options.proxyOptions.proxyHost, - port: this.options.proxyOptions.proxyPort || 1080 - }); + port: this.options.proxyOptions.proxyPort || 1080, + ...socketOptions + }; + netSocket.connect(netSocketOptions); await willConnect; try { diff --git a/src/cmap/connect.ts b/src/cmap/connect.ts index 58c3519fed..b2ec3caabf 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', diff --git a/src/connection_string.ts b/src/connection_string.ts index b0becafac0..bf9acf556c 100644 --- a/src/connection_string.ts +++ b/src/connection_string.ts @@ -740,6 +740,13 @@ export const OPTIONS = { autoEncryption: { type: 'record' }, + autoSelectFamily: { + type: 'boolean', + default: true + }, + autoSelectFamilyAttemptTimeout: { + type: 'uint' + }, bsonRegExp: { type: 'boolean' }, diff --git a/src/index.ts b/src/index.ts index 0ba8f82c01..efd0b9d055 100644 --- a/src/index.ts +++ b/src/index.ts @@ -248,6 +248,7 @@ export type { LocalKMSProviderConfiguration } from './client-side-encryption/providers/index'; export type { + ClientEncryptionSocketOptions, ClientEncryptionTlsOptions, CSFLEKMSTlsOptions, StateMachineExecutable 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); diff --git a/test/unit/client-side-encryption/auto_encrypter.test.ts b/test/unit/client-side-encryption/auto_encrypter.test.ts index 08072512c7..1e13c0b07c 100644 --- a/test/unit/client-side-encryption/auto_encrypter.test.ts +++ b/test/unit/client-side-encryption/auto_encrypter.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import * as fs from 'fs'; +import * as net from 'net'; import * as sinon from 'sinon'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports @@ -37,7 +38,13 @@ const MOCK_MONGOCRYPTD_RESPONSE = readExtendedJsonToBuffer( const MOCK_KEYDOCUMENT_RESPONSE = readExtendedJsonToBuffer(`${__dirname}/data/key-document.json`); const MOCK_KMS_DECRYPT_REPLY = readHttpResponse(`${__dirname}/data/kms-decrypt-reply.txt`); -class MockClient {} +class MockClient { + options: any; + + constructor(options?: any) { + this.options = { options: options || {} }; + } +} const originalAccessKeyId = process.env.AWS_ACCESS_KEY_ID; const originalSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY; @@ -105,15 +112,19 @@ describe('AutoEncrypter', function () { }); context('when mongocryptdURI is not specified', () => { - it('sets the ip address family to ipv4', function () { + it('sets family options', function () { expect(autoEncrypter).to.have.nested.property('_mongocryptdClient.s.options'); const options = autoEncrypter._mongocryptdClient?.s.options; - expect(options).to.have.property('family', 4); + if (net.getDefaultAutoSelectFamily) { + expect(options).to.have.property('autoSelectFamily', true); + } else { + expect(options).to.have.property('family', 4); + } }); }); context('when mongocryptdURI is specified', () => { - it('does not set the ip address family to ipv4', function () { + it('sets autoSelectFamily options', function () { const autoEncrypter = new AutoEncrypter(client, { ...autoEncrypterOptions, extraOptions: { mongocryptdURI: MongocryptdManager.DEFAULT_MONGOCRYPTD_URI } @@ -121,7 +132,7 @@ describe('AutoEncrypter', function () { expect(autoEncrypter).to.have.nested.property('_mongocryptdClient.s.options'); const options = autoEncrypter._mongocryptdClient?.s.options; - expect(options).not.to.have.property('family', 4); + expect(options).to.have.property('autoSelectFamily', true); }); }); }); diff --git a/test/unit/client-side-encryption/client_encryption.test.ts b/test/unit/client-side-encryption/client_encryption.test.ts index c83383d4e4..2ecf634771 100644 --- a/test/unit/client-side-encryption/client_encryption.test.ts +++ b/test/unit/client-side-encryption/client_encryption.test.ts @@ -19,6 +19,11 @@ import { Binary, BSON, deserialize } from '../../mongodb'; const { EJSON } = BSON; class MockClient { + options: any; + + constructor(options?: any) { + this.options = { options: options || {} }; + } db(dbName) { return { async createCollection(name, options) { diff --git a/test/unit/client-side-encryption/state_machine.test.ts b/test/unit/client-side-encryption/state_machine.test.ts index 518e63a26d..baec0cbece 100644 --- a/test/unit/client-side-encryption/state_machine.test.ts +++ b/test/unit/client-side-encryption/state_machine.test.ts @@ -148,6 +148,30 @@ describe('StateMachine', function () { }); }); + context('when socket options are provided', function () { + const stateMachine = new StateMachine({ + socketOptions: { autoSelectFamily: true, autoSelectFamilyAttemptTimeout: 300 } + } as any); + const request = new MockRequest(Buffer.from('foobar'), -1); + let connectOptions; + + it('passes them through to the socket', async function () { + sandbox.stub(tls, 'connect').callsFake((options, callback) => { + connectOptions = options; + this.fakeSocket = new MockSocket(callback); + return this.fakeSocket; + }); + const kmsRequestPromise = stateMachine.kmsRequest(request); + + await setTimeoutAsync(0); + this.fakeSocket.emit('data', Buffer.alloc(0)); + + await kmsRequestPromise; + expect(connectOptions.autoSelectFamily).to.equal(true); + expect(connectOptions.autoSelectFamilyAttemptTimeout).to.equal(300); + }); + }); + context('when tls options are provided', function () { context('when the options are insecure', function () { [