tls: forward keepAlive, keepAliveInitialDelay, noDelay to socket

`tls.connect()` silently ignores `keepAlive`, `keepAliveInitialDelay`,
and `noDelay` options. The documentation states it accepts any
`socket.connect()` option, and `net.createConnection()` with the same
options works correctly.

Forward the options through both code paths so `net.Socket`'s
constructor stores them on the internal symbols (`kSetNoDelay`,
`kSetKeepAlive`, `kSetKeepAliveInitialDelay`), which `afterConnect()`
then applies to the handle.

Fixes: https://github.com/nodejs/node/issues/62003
PR-URL: https://github.com/nodejs/node/pull/62004
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
This commit is contained in:
Sergey Zelenov authored and GitHub committed 2026-02-28 11:22:35 +00:00
1 parent c9d0ef8b24
commit 6a3d358ca4
4 files changed
+77 -3

No files matched your search

+3
View File
@@ -100,6 +100,9 @@ function isLoopback(host) {
module.exports = {
kReinitializeHandle: Symbol('kReinitializeHandle'),
kSetNoDelay: Symbol('kSetNoDelay'),
kSetKeepAlive: Symbol('kSetKeepAlive'),
kSetKeepAliveInitialDelay: Symbol('kSetKeepAliveInitialDelay'),
isIP,
isIPv4,
isIPv6,
+6
View File
@@ -590,6 +590,9 @@ function TLSSocket(socket, opts) {
highWaterMark: tlsOptions.highWaterMark,
onread: !socket ? tlsOptions.onread : null,
signal: tlsOptions.signal,
noDelay: tlsOptions.noDelay,
keepAlive: tlsOptions.keepAlive,
keepAliveInitialDelay: tlsOptions.keepAliveInitialDelay,
});
// Proxy for API compatibility
@@ -1755,6 +1758,9 @@ exports.connect = function connect(...args) {
highWaterMark: options.highWaterMark,
onread: options.onread,
signal: options.signal,
noDelay: options.noDelay,
keepAlive: options.keepAlive,
keepAliveInitialDelay: options.keepAliveInitialDelay,
});
// rejectUnauthorized property can be explicitly defined as `undefined`
+3 -3
View File
@@ -48,6 +48,9 @@ let debug = require('internal/util/debuglog').debuglog('net', (fn) => {
});
const {
kReinitializeHandle,
kSetNoDelay,
kSetKeepAlive,
kSetKeepAliveInitialDelay,
isIP,
isIPv4,
isIPv6,
@@ -356,9 +359,6 @@ function closeSocketHandle(self, isException, isCleanupPending = false) {
const kBytesRead = Symbol('kBytesRead');
const kBytesWritten = Symbol('kBytesWritten');
const kSetNoDelay = Symbol('kSetNoDelay');
const kSetKeepAlive = Symbol('kSetKeepAlive');
const kSetKeepAliveInitialDelay = Symbol('kSetKeepAliveInitialDelay');
const kSetTOS = Symbol('kSetTOS');
function Socket(options) {
@@ -0,0 +1,65 @@
// Flags: --expose-internals
'use strict';
const common = require('../common');
// This test verifies that tls.connect() forwards keepAlive,
// keepAliveInitialDelay, and noDelay options to the underlying socket.
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const tls = require('tls');
const fixtures = require('../common/fixtures');
const {
kSetNoDelay,
kSetKeepAlive,
kSetKeepAliveInitialDelay,
} = require('internal/net');
const key = fixtures.readKey('agent1-key.pem');
const cert = fixtures.readKey('agent1-cert.pem');
// Test: keepAlive, keepAliveInitialDelay, and noDelay
{
const server = tls.createServer({ key, cert }, (socket) => {
socket.end();
});
server.listen(0, common.mustCall(() => {
const socket = tls.connect({
port: server.address().port,
rejectUnauthorized: false,
keepAlive: true,
keepAliveInitialDelay: 1000,
noDelay: true,
}, common.mustCall(() => {
assert.strictEqual(socket[kSetKeepAlive], true);
assert.strictEqual(socket[kSetKeepAliveInitialDelay], 1);
assert.strictEqual(socket[kSetNoDelay], true);
socket.destroy();
server.close();
}));
}));
}
// Test: defaults (options not set)
{
const server = tls.createServer({ key, cert }, (socket) => {
socket.end();
});
server.listen(0, common.mustCall(() => {
const socket = tls.connect({
port: server.address().port,
rejectUnauthorized: false,
}, common.mustCall(() => {
assert.strictEqual(socket[kSetKeepAlive], false);
assert.strictEqual(socket[kSetKeepAliveInitialDelay], 0);
assert.strictEqual(socket[kSetNoDelay], false);
socket.destroy();
server.close();
}));
}));
}