Title: doc: tls.TLSSocket emits 'secure' event but it is undocumented · Issue #61060 · nodejs/node · GitHub
Open Graph Title: doc: tls.TLSSocket emits 'secure' event but it is undocumented · Issue #61060 · nodejs/node
X Title: doc: tls.TLSSocket emits 'secure' event but it is undocumented · Issue #61060 · nodejs/node
Description: 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, callbac...
Open Graph Description: 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 fo...
X Description: What is the problem? The tls.TLSSocket documentation does not list an Event: 'secure' entry (only 'keylog', 'OCSPResponse', 'secureConnect', and 'session' ar...
Opengraph URL: https://github.com/nodejs/node/issues/61060
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"doc: tls.TLSSocket emits 'secure' event but it is undocumented","articleBody":"### What is the problem?\n\nThe `tls.TLSSocket` documentation does not list an `Event: 'secure'` entry (only `'keylog'`, `'OCSPResponse'`, `'secureConnect'`, and `'session'` are listed).\n\nHowever, the documentation for `tlsSocket.renegotiate(options, callback)` explicitly refers to the `'secure'` event:\n\n\u003e If `renegotiate()` returned `true`, callback is attached once to the `'secure'` event.\n\nThis creates a dangling reference: the docs mention `'secure'`, but the TLSSocket event is not documented.\n\nAffected documentation:\n\n- https://nodejs.org/docs/latest-v24.x/api/tls.html#class-tlstlssocket\n- https://nodejs.org/docs/latest-v24.x/api/tls.html#tlssocketrenegotiateoptions-callback\n\n### What is expected?\n\nPlease document `Event: 'secure'` under `Class: tls.TLSSocket` events, and clarify when it is emitted.\nAt minimum, documenting its existence would resolve the current dangling reference from `tlsSocket.renegotiate()`.\n\nAlternatively, please remove the mention from `tlsSocket.renegotiate()`.\n\n---\n\n### Observed behavior / reproduction\n\nIn the following test, `'secure'` is emitted on both sides:\n\n- Server-side TLSSocket (created via `new tls.TLSSocket(raw, { isServer: true, ... })`) emits `secure` once\n- Client-side TLSSocket (from `tls.connect()`) emits `secure` once and `secureConnect` once\n\n\u003cdetails\u003e\n\u003csummary\u003eTest code\u003c/summary\u003e\n\n```sh\n# generate server.key\nopenssl genrsa -out server.key 2048\n# generate server.crt\nopenssl req -x509 -new -nodes \\\n -key server.key \\\n -sha256 \\\n -days 1 \\\n -out server.crt \\\n -subj \"/CN=ocsp.example.test\" \\\n -addext \"subjectAltName=DNS:ocsp.example.test\"\n```\n\nAfter that, execute the following typescript in the same directory:\n```ts\nimport * as assert from \"node:assert\";\nimport * as fs from \"node:fs\";\nimport * as net from \"node:net\";\nimport { describe, it } from \"node:test\";\nimport * as tls from \"node:tls\";\n\nconst domain = \"ocsp.example.test\";\n\nconst certPem = fs.readFileSync(\"server.crt\");\nconst keyPem = fs.readFileSync(\"server.key\");\n\nasync function listen(server: net.Server) {\n await new Promise\u003cvoid\u003e((resolve, reject) =\u003e {\n server.once(\"error\", reject);\n server.listen(0, \"127.0.0.1\", resolve);\n });\n const addr = server.address();\n assert.ok(addr \u0026\u0026 typeof addr === \"object\");\n return addr.port;\n}\n\ntype EventName = \"secure\" | \"secureConnect\";\ntype EventCounts = Record\u003cEventName, number\u003e;\nfunction traceEvents(tlsSocket: tls.TLSSocket) {\n const events: EventCounts = { secure: 0, secureConnect: 0 };\n tlsSocket.on(\"secure\", () =\u003e {\n events.secure += 1;\n });\n tlsSocket.on(\"secureConnect\", () =\u003e {\n events.secureConnect += 1;\n });\n return events;\n}\nfunction connectClient(port: number) {\n return new Promise\u003cEventCounts\u003e((resolve, reject) =\u003e {\n const s = tls.connect({\n host: \"127.0.0.1\",\n port,\n servername: domain,\n rejectUnauthorized: false,\n minVersion: \"TLSv1.2\",\n maxVersion: \"TLSv1.2\",\n });\n\n const events = traceEvents(s);\n s.once(\"secureConnect\", () =\u003e {\n s.end();\n });\n s.once(\"close\", () =\u003e resolve(events));\n s.once(\"error\", reject);\n s.setTimeout(3_000, () =\u003e s.destroy(new Error(\"TLS handshake timeout\")));\n });\n}\n\ndescribe(\"undocumented TLSSocket event: 'secure'\", () =\u003e {\n it(\"'secure' is emitted on both server-side and client-side TLSSocket\", async () =\u003e {\n let serverEvents: EventCounts | undefined;\n\n await using server = net.createServer((raw) =\u003e {\n const tlsSocket = new tls.TLSSocket(raw, {\n isServer: true,\n cert: certPem,\n key: keyPem,\n minVersion: \"TLSv1.2\",\n maxVersion: \"TLSv1.2\",\n });\n\n serverEvents = traceEvents(tlsSocket);\n tlsSocket.once(\"error\", (e) =\u003e raw.destroy(e));\n tlsSocket.once(\"secure\", () =\u003e tlsSocket.end());\n });\n const port = await listen(server);\n const clientEvents = await connectClient(port);\n assert.ok(serverEvents, \"serverEvents should be set\");\n\n // ✅ TLSSocket fires 'secure' on both sides\n // (server-side TLSSocket does not fire 'secureConnect')\n assert.deepStrictEqual(\n serverEvents,\n { secure: 1, secureConnect: 0 },\n \"server-side TLSSocket should emit 'secure' exactly once\",\n );\n assert.deepStrictEqual(\n clientEvents,\n { secure: 1, secureConnect: 1 },\n \"client-side TLSSocket should emit 'secureConnect' and 'secure' exactly once each\",\n );\n });\n});\n```\n\u003c/details\u003e\n","author":{"url":"https://github.com/ikeyan","@type":"Person","name":"ikeyan"},"datePublished":"2025-12-14T15:44:39.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/61060/node/issues/61060"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:d17cd0ac-127a-6e61-c104-a237ffe7f693 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | A442:20AB3F:BA6C66:10F7D70:6A4CC127 |
| html-safe-nonce | 6b66cc979eee3a4c3533add16695f2562164c9afe847e9278f555e6259268603 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBNDQyOjIwQUIzRjpCQTZDNjY6MTBGN0Q3MDo2QTRDQzEyNyIsInZpc2l0b3JfaWQiOiI1MjkzOTY3MDc0NDAxMjM5MzM1IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 97d6e2f4d4745449df0cd8162b3bf4805797e9915b03ebb0eecdc51a444a56aa |
| hovercard-subject-tag | issue:3727605020 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/61060/issue_layout |
| twitter:image | https://opengraph.githubassets.com/5932210b42101c3ef6dd6e24a5e77ae8a0f74d06f40f7dd0e11c409bfe5f4acc/nodejs/node/issues/61060 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/5932210b42101c3ef6dd6e24a5e77ae8a0f74d06f40f7dd0e11c409bfe5f4acc/nodejs/node/issues/61060 |
| og:image:alt | 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 fo... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | ikeyan |
| hostname | github.com |
| expected-hostname | github.com |
| None | 3d11bb817438277de2a940854450e83a7d32b6aeb5014e9e6b00a6423900251c |
| turbo-cache-control | no-preview |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | c03e7e569190bc89b638cdd4acb4b6c6b38a170a |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width