Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/cmap/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ export const LEGAL_TLS_SOCKET_OPTIONS = [
export const LEGAL_TCP_SOCKET_OPTIONS = [
'autoSelectFamily',
'autoSelectFamilyAttemptTimeout',
'keepAliveInitialDelay',
'family',
'hints',
'localAddress',
Expand All @@ -306,6 +307,11 @@ function parseConnectOptions(options: ConnectionOptions): SocketConnectOpts {
(result as Document)[name] = options[name];
}
}
if (result.keepAliveInitialDelay == null) {
result.keepAliveInitialDelay = 120000;
}
result.keepAlive = true;
result.noDelay = options.noDelay ?? true;

if (typeof hostAddress.socketPath === 'string') {
result.path = hostAddress.socketPath;
Expand Down Expand Up @@ -347,7 +353,6 @@ function parseSslOptions(options: MakeConnectionOptions): TLSConnectionOpts {

export async function makeSocket(options: MakeConnectionOptions): Promise<Stream> {
const useTLS = options.tls ?? false;
const noDelay = options.noDelay ?? true;
const connectTimeoutMS = options.connectTimeoutMS ?? 30000;
const existingSocket = options.existingSocket;

Expand Down Expand Up @@ -376,9 +381,7 @@ export async function makeSocket(options: MakeConnectionOptions): Promise<Stream
socket = net.createConnection(parseConnectOptions(options));
}

socket.setKeepAlive(true, 300000);
socket.setTimeout(connectTimeoutMS);
socket.setNoDelay(noDelay);

let cancellationHandler: ((err: Error) => void) | null = null;

Expand Down
1 change: 1 addition & 0 deletions src/connection_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,7 @@ export const OPTIONS = {
requestCert: { type: 'any' },
rejectUnauthorized: { type: 'any' },
checkServerIdentity: { type: 'any' },
keepAliveInitialDelay: { type: 'any' },
ALPNProtocols: { type: 'any' },
SNICallback: { type: 'any' },
session: { type: 'any' },
Expand Down
7 changes: 6 additions & 1 deletion src/mongo_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,12 @@ export type SupportedTLSSocketOptions = Pick<

/** @public */
export type SupportedSocketOptions = Pick<
TcpNetConnectOpts & { autoSelectFamily?: boolean; autoSelectFamilyAttemptTimeout?: number },
TcpNetConnectOpts & {
autoSelectFamily?: boolean;
autoSelectFamilyAttemptTimeout?: number;
/** Node.JS socket option to set the time the first keepalive probe is sent on an idle socket. */
keepAliveInitialDelay?: number;
},
(typeof LEGAL_TCP_SOCKET_OPTIONS)[number]
>;

Expand Down
182 changes: 171 additions & 11 deletions test/integration/node-specific/mongo_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,166 @@ describe('class MongoClient', function () {
expect(error).to.be.instanceOf(MongoServerSelectionError);
});

describe('#connect', function () {
context('when keepAliveInitialDelay is provided', function () {
context('when the value is 0', function () {
const options = { keepAliveInitialDelay: 0 };
let client;
let spy;

beforeEach(async function () {
spy = sinon.spy(net, 'createConnection');
const uri = this.configuration.url();
client = new MongoClient(uri, options);
await client.connect();
});

afterEach(async function () {
await client?.close();
spy.restore();
});

it('passes through the option', function () {
expect(spy).to.have.been.calledWith(
sinon.match({
keepAlive: true,
keepAliveInitialDelay: 0
})
);
});
});

context('when the value is positive', function () {
const options = { keepAliveInitialDelay: 100 };
let client;
let spy;

beforeEach(async function () {
spy = sinon.spy(net, 'createConnection');
const uri = this.configuration.url();
client = new MongoClient(uri, options);
await client.connect();
});

afterEach(async function () {
await client?.close();
spy.restore();
});

it('passes through the option', function () {
expect(spy).to.have.been.calledWith(
sinon.match({
keepAlive: true,
keepAliveInitialDelay: 100
})
);
});
});

context('when the value is negative', function () {
const options = { keepAliveInitialDelay: -100 };
let client;
let spy;

beforeEach(async function () {
spy = sinon.spy(net, 'createConnection');
const uri = this.configuration.url();
client = new MongoClient(uri, options);
await client.connect();
});

afterEach(async function () {
await client?.close();
spy.restore();
});

it('sets the option to 0', function () {
expect(spy).to.have.been.calledWith(
sinon.match({
keepAlive: true,
keepAliveInitialDelay: 0
})
);
});
});
});

context('when keepAliveInitialDelay is not provided', function () {
let client;
let spy;

beforeEach(async function () {
spy = sinon.spy(net, 'createConnection');
client = this.configuration.newClient();
await client.connect();
});

afterEach(async function () {
await client?.close();
spy.restore();
});

it('sets keepalive to 120000', function () {
expect(spy).to.have.been.calledWith(
sinon.match({
keepAlive: true,
keepAliveInitialDelay: 120000
})
);
});
});

context('when noDelay is not provided', function () {
let client;
let spy;

beforeEach(async function () {
spy = sinon.spy(net, 'createConnection');
client = this.configuration.newClient();
await client.connect();
});

afterEach(async function () {
await client?.close();
spy.restore();
});

it('sets noDelay to true', function () {
expect(spy).to.have.been.calledWith(
sinon.match({
noDelay: true
})
);
});
});

context('when noDelay is provided', function () {
let client;
let spy;

beforeEach(async function () {
const options = { noDelay: false };
spy = sinon.spy(net, 'createConnection');
const uri = this.configuration.url();
client = new MongoClient(uri, options);
await client.connect();
});

afterEach(async function () {
await client?.close();
spy.restore();
});

it('sets noDelay', function () {
expect(spy).to.have.been.calledWith(
sinon.match({
noDelay: false
})
);
});
});
});

it('Should correctly pass through appname', {
metadata: {
requires: {
Expand Down Expand Up @@ -889,12 +1049,12 @@ describe('class MongoClient', function () {
metadata: { requires: { topology: ['single'] } },
test: async function () {
await client.connect();
expect(netSpy).to.have.been.calledWith({
autoSelectFamily: false,
autoSelectFamilyAttemptTimeout: 100,
host: 'localhost',
port: 27017
});
expect(netSpy).to.have.been.calledWith(
sinon.match({
autoSelectFamily: false,
autoSelectFamilyAttemptTimeout: 100
})
);
}
});
});
Expand All @@ -908,11 +1068,11 @@ describe('class MongoClient', function () {
metadata: { requires: { topology: ['single'] } },
test: async function () {
await client.connect();
expect(netSpy).to.have.been.calledWith({
autoSelectFamily: true,
host: 'localhost',
port: 27017
});
expect(netSpy).to.have.been.calledWith(
sinon.match({
autoSelectFamily: true
})
);
}
});
});
Expand Down