feat(tsconnect/wasm): add upgradeTLS to wrapped conns #14

Merged
codinget merged 2 commits from feat/conn-upgrade-tls into webnet 2026-07-12 15:11:33 +02:00
Owner

Adds an upgradeTLS method to the conn objects returned by dial/accept, wrapping the existing net.Conn with crypto/tls in place — the explicit upgrade needed for STARTTLS (SMTP) and AUTH TLS (FTPS).

  • tlsClientConfigFromJS factored out of dialTLS (behavior unchanged); when no default server name is available (the upgrade case) it requires serverName unless insecureSkipVerify is set.
  • upgradeTLS(opts) on wrapped conns: client mode uses the same options as dialTLS (serverName, insecureSkipVerify, caCerts); server mode via isServer: true with certPem/keyPem (same parsing as listenTLS).
  • Returns a new wrapped conn sharing the same underlying net.Conn; the old handle must not be used afterward. On any failure — configuration (malformed PEM) or handshake — the underlying conn is closed (721194d, after external review: previously config errors left the conn open while the JS wrapper marked it closed, leaking it).

Consumed by the corresponding @webnet/transport / @webnet/tsconnect PR on the webnet repo (webnet/webnet#63), which provides the in-place RawTransport.upgradeTls semantics one layer up.

Adds an `upgradeTLS` method to the conn objects returned by `dial`/accept, wrapping the existing `net.Conn` with `crypto/tls` in place — the explicit upgrade needed for STARTTLS (SMTP) and AUTH TLS (FTPS). - `tlsClientConfigFromJS` factored out of `dialTLS` (behavior unchanged); when no default server name is available (the upgrade case) it requires `serverName` unless `insecureSkipVerify` is set. - `upgradeTLS(opts)` on wrapped conns: client mode uses the same options as `dialTLS` (`serverName`, `insecureSkipVerify`, `caCerts`); server mode via `isServer: true` with `certPem`/`keyPem` (same parsing as `listenTLS`). - Returns a new wrapped conn sharing the same underlying `net.Conn`; the old handle must not be used afterward. On **any** failure — configuration (malformed PEM) or handshake — the underlying conn is closed (721194d, after external review: previously config errors left the conn open while the JS wrapper marked it closed, leaking it). Consumed by the corresponding `@webnet/transport` / `@webnet/tsconnect` PR on the webnet repo (webnet/webnet#63), which provides the in-place `RawTransport.upgradeTls` semantics one layer up.
codinget added 1 commit 2026-07-06 14:40:56 +02:00
Adds an upgradeTLS method to the conn objects returned by dial/accept,
wrapping the existing net.Conn with crypto/tls in place (STARTTLS /
FTPS AUTH TLS style). Client mode reuses the dialTLS options
(serverName, insecureSkipVerify, caCerts), now factored into
tlsClientConfigFromJS; server mode takes isServer with certPem/keyPem.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfRgMke8p7QDxD5QM5thxH
codinget added the
Agent
claude-fable-5
1
Agentic
labels 2026-07-06 14:41:13 +02:00
Author
Owner

Reviewed alongside webnet/webnet#63 (the consumer of upgradeTLS). The Go side here looks correct in isolation, but flagging for context: the new wrapConn(tlsConn) returned by upgradeTLS (around line 1484 in cmd/tsconnect/wasm/wasm_js.go) leaves the old wrapConn's read/write closures alive, still bound to the plain net.Conn that tls.Client/tls.Server now uses internally. That's fine as long as the JS caller strictly stops using the old handle the instant it calls upgradeTLS — but webnet/webnet#63's ipn.ts Conn.upgradeTls doesn't actually enforce that (it doesn't mark itself in-flight during the handshake), so a concurrent read()/write() from the JS side can still reach the old handle and race the handshake on the same fd. Posted the detailed finding and suggested fix on webnet#63; no action needed here unless you want to harden this side too (e.g. by having the old wrapConn's closures start erroring once superseded).

Reviewed alongside webnet/webnet#63 (the consumer of upgradeTLS). The Go side here looks correct in isolation, but flagging for context: the new `wrapConn(tlsConn)` returned by `upgradeTLS` (around line 1484 in cmd/tsconnect/wasm/wasm_js.go) leaves the *old* wrapConn's read/write closures alive, still bound to the plain `net.Conn` that `tls.Client`/`tls.Server` now uses internally. That's fine as long as the JS caller strictly stops using the old handle the instant it calls upgradeTLS — but webnet/webnet#63's `ipn.ts Conn.upgradeTls` doesn't actually enforce that (it doesn't mark itself in-flight during the handshake), so a concurrent read()/write() from the JS side can still reach the old handle and race the handshake on the same fd. Posted the detailed finding and suggested fix on webnet#63; no action needed here unless you want to harden this side too (e.g. by having the old wrapConn's closures start erroring once superseded).
codinget added 1 commit 2026-07-12 13:28:36 +02:00
Previously only a handshake failure closed the underlying conn;
configuration errors (malformed caCerts PEM, bad cert/key pair)
returned with the conn still open, while the JS wrapper treats every
upgradeTLS rejection as fatal and marks the conn closed — leaking a
live Go conn that could no longer be closed from JS. Close on every
error path so the JS contract (any rejection after validation is
fatal) holds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfRgMke8p7QDxD5QM5thxH
codinget marked the pull request as ready for review 2026-07-12 15:10:44 +02:00
codinget merged commit 721194dfb5 into webnet 2026-07-12 15:11:33 +02:00
codinget deleted branch feat/conn-upgrade-tls 2026-07-12 15:11:33 +02:00
Sign in to join this conversation.