Skip to content

doc: tls.TLSSocket emits 'secure' event but it is undocumented #61060

@ikeyan

Description

@ikeyan

What is the problem?

The tls.TLSSocket documentation does not list an Event: 'secure' entry (only 'keylog', 'OCSPResponse', 'secureConnect', and 'session' are listed).

However, the documentation for tlsSocket.renegotiate(options, callback) explicitly refers to the 'secure' event:

If renegotiate() returned true, callback is attached once to the 'secure' event.

This creates a dangling reference: the docs mention 'secure', but the TLSSocket event is not documented.

Affected documentation:

What is expected?

Please document Event: 'secure' under Class: tls.TLSSocket events, and clarify when it is emitted.
At minimum, documenting its existence would resolve the current dangling reference from tlsSocket.renegotiate().

Alternatively, please remove the mention from tlsSocket.renegotiate().


Observed behavior / reproduction

In the following test, 'secure' is emitted on both sides:

  • Server-side TLSSocket (created via new tls.TLSSocket(raw, { isServer: true, ... })) emits secure once
  • Client-side TLSSocket (from tls.connect()) emits secure once and secureConnect once
Test code
# generate server.key
openssl genrsa -out server.key 2048
# generate server.crt
openssl req -x509 -new -nodes \
  -key server.key \
  -sha256 \
  -days 1 \
  -out server.crt \
  -subj "/CN=ocsp.example.test" \
  -addext "subjectAltName=DNS:ocsp.example.test"

After that, execute the following typescript in the same directory:

import * as assert from "node:assert";
import * as fs from "node:fs";
import * as net from "node:net";
import { describe, it } from "node:test";
import * as tls from "node:tls";

const domain = "ocsp.example.test";

const certPem = fs.readFileSync("server.crt");
const keyPem = fs.readFileSync("server.key");

async function listen(server: net.Server) {
    await new Promise<void>((resolve, reject) => {
        server.once("error", reject);
        server.listen(0, "127.0.0.1", resolve);
    });
    const addr = server.address();
    assert.ok(addr && typeof addr === "object");
    return addr.port;
}

type EventName = "secure" | "secureConnect";
type EventCounts = Record<EventName, number>;
function traceEvents(tlsSocket: tls.TLSSocket) {
    const events: EventCounts = { secure: 0, secureConnect: 0 };
    tlsSocket.on("secure", () => {
        events.secure += 1;
    });
    tlsSocket.on("secureConnect", () => {
        events.secureConnect += 1;
    });
    return events;
}
function connectClient(port: number) {
    return new Promise<EventCounts>((resolve, reject) => {
        const s = tls.connect({
            host: "127.0.0.1",
            port,
            servername: domain,
            rejectUnauthorized: false,
            minVersion: "TLSv1.2",
            maxVersion: "TLSv1.2",
        });

        const events = traceEvents(s);
        s.once("secureConnect", () => {
            s.end();
        });
        s.once("close", () => resolve(events));
        s.once("error", reject);
        s.setTimeout(3_000, () => s.destroy(new Error("TLS handshake timeout")));
    });
}

describe("undocumented TLSSocket event: 'secure'", () => {
    it("'secure' is emitted on both server-side and client-side TLSSocket", async () => {
        let serverEvents: EventCounts | undefined;

        await using server = net.createServer((raw) => {
            const tlsSocket = new tls.TLSSocket(raw, {
                isServer: true,
                cert: certPem,
                key: keyPem,
                minVersion: "TLSv1.2",
                maxVersion: "TLSv1.2",
            });

            serverEvents = traceEvents(tlsSocket);
            tlsSocket.once("error", (e) => raw.destroy(e));
            tlsSocket.once("secure", () => tlsSocket.end());
        });
        const port = await listen(server);
        const clientEvents = await connectClient(port);
        assert.ok(serverEvents, "serverEvents should be set");

        // ✅ TLSSocket fires 'secure' on both sides
        // (server-side TLSSocket does not fire 'secureConnect')
        assert.deepStrictEqual(
            serverEvents,
            { secure: 1, secureConnect: 0 },
            "server-side TLSSocket should emit 'secure' exactly once",
        );
        assert.deepStrictEqual(
            clientEvents,
            { secure: 1, secureConnect: 1 },
            "client-side TLSSocket should emit 'secureConnect' and 'secure' exactly once each",
        );
    });
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions