@@ -22,6 +22,7 @@ import type {
2222 NetSocketWriteRawBridgeRef ,
2323 NetSocketEndRawBridgeRef ,
2424 NetSocketDestroyRawBridgeRef ,
25+ NetSocketUpgradeTlsRawBridgeRef ,
2526} from "../shared/bridge-contract.js" ;
2627
2728// Declare host bridge References
@@ -67,6 +68,10 @@ declare const _netSocketDestroyRaw:
6768 | NetSocketDestroyRawBridgeRef
6869 | undefined ;
6970
71+ declare const _netSocketUpgradeTlsRaw :
72+ | NetSocketUpgradeTlsRawBridgeRef
73+ | undefined ;
74+
7075declare const _registerHandle :
7176 | RegisterHandleBridgeFn
7277 | undefined ;
@@ -1957,7 +1962,8 @@ class NetSocket {
19571962 bytesRead = 0 ;
19581963 bytesWritten = 0 ;
19591964 private _listeners : Record < string , EventListener [ ] > = { } ;
1960- private _socketId = - 1 ;
1965+ /** @internal socket ID shared with TLS upgrade bridge */
1966+ _socketId = - 1 ;
19611967 private _connectHost = "" ;
19621968 private _connectPort = 0 ;
19631969
@@ -2213,6 +2219,7 @@ function onNetSocketDispatch(socketId: number, type: string, data: string): void
22132219 case "end" : socket . _onEnd ( ) ; break ;
22142220 case "error" : socket . _onError ( data ) ; break ;
22152221 case "close" : socket . _onClose ( data === "1" ) ; break ;
2222+ case "secureConnect" : socket . emit ( "secureConnect" ) ; break ;
22162223 }
22172224}
22182225
@@ -2248,12 +2255,101 @@ export const net = {
22482255 isIPv6 ( input : string ) : boolean { return netIsIP ( input ) === 6 ; } ,
22492256} ;
22502257
2258+ // ----------------------------------------------------------------
2259+ // tls module — TLS socket upgrade bridge
2260+ // ----------------------------------------------------------------
2261+
2262+ /** TLS socket that wraps an existing NetSocket after host-side TLS upgrade. */
2263+ class TLSSocket extends NetSocket {
2264+ encrypted = true ;
2265+ authorized = false ;
2266+ authorizationError : string | null = null ;
2267+ alpnProtocol : string | false = false ;
2268+ private _wrappedSocket : NetSocket | null = null ;
2269+
2270+ constructor ( originalSocket : NetSocket ) {
2271+ super ( ) ;
2272+ this . _wrappedSocket = originalSocket ;
2273+ // Copy connection state from original socket
2274+ this . remoteAddress = originalSocket . remoteAddress ;
2275+ this . remotePort = originalSocket . remotePort ;
2276+ this . remoteFamily = originalSocket . remoteFamily ;
2277+ this . localAddress = originalSocket . localAddress ;
2278+ this . localPort = originalSocket . localPort ;
2279+ this . connecting = false ;
2280+ this . pending = false ;
2281+ this . readyState = "open" ;
2282+ // Share the same socketId — bridge events route here after upgrade
2283+ this . _socketId = originalSocket . _socketId ;
2284+ // Copy private connect info so _cleanup unregisters the correct handle
2285+ ( this as Record < string , unknown > ) . _connectHost = ( originalSocket as Record < string , unknown > ) . _connectHost ;
2286+ ( this as Record < string , unknown > ) . _connectPort = ( originalSocket as Record < string , unknown > ) . _connectPort ;
2287+ netSocketInstances . set ( this . _socketId , this ) ;
2288+ }
2289+
2290+ _onSecureConnect ( ) : void {
2291+ this . authorized = true ;
2292+ this . emit ( "secureConnect" ) ;
2293+ }
2294+
2295+ // Forward end/close to the wrapped raw socket — Node.js tls.TLSSocket
2296+ // closes the underlying socket, which fires its 'close' event. Libraries
2297+ // like pg rely on the original socket's 'close' listener to detect shutdown.
2298+ _onEnd ( ) : void {
2299+ super . _onEnd ( ) ;
2300+ if ( this . _wrappedSocket ) this . _wrappedSocket . _onEnd ( ) ;
2301+ }
2302+
2303+ _onClose ( hadError : boolean ) : void {
2304+ super . _onClose ( hadError ) ;
2305+ if ( this . _wrappedSocket ) {
2306+ this . _wrappedSocket . _onClose ( hadError ) ;
2307+ this . _wrappedSocket = null ;
2308+ }
2309+ }
2310+ }
2311+
2312+ export const tlsModule = {
2313+ TLSSocket : TLSSocket as unknown as typeof import ( "tls" ) . TLSSocket ,
2314+ connect ( options : Record < string , unknown > ) : NetSocket {
2315+ const existingSocket = options . socket as NetSocket | undefined ;
2316+ if ( ! existingSocket || existingSocket . _socketId < 0 ) {
2317+ throw new Error ( "tls.connect requires an existing connected socket via options.socket" ) ;
2318+ }
2319+
2320+ // Create TLS socket wrapper on sandbox side
2321+ const tlsSocket = new TLSSocket ( existingSocket ) ;
2322+
2323+ if ( typeof _netSocketUpgradeTlsRaw === "undefined" ) {
2324+ Promise . resolve ( ) . then ( ( ) => {
2325+ tlsSocket . _onError ( "tls.connect requires NetworkAdapter TLS support" ) ;
2326+ } ) ;
2327+ return tlsSocket ;
2328+ }
2329+
2330+ // Tell host to wrap the underlying TCP socket with TLS
2331+ _netSocketUpgradeTlsRaw . applySync ( undefined , [
2332+ existingSocket . _socketId ,
2333+ JSON . stringify ( {
2334+ rejectUnauthorized : options . rejectUnauthorized ?? true ,
2335+ servername : options . servername ,
2336+ } ) ,
2337+ ] ) ;
2338+
2339+ return tlsSocket ;
2340+ } ,
2341+ createSecureContext ( _options ?: Record < string , unknown > ) : Record < string , unknown > {
2342+ return { } ;
2343+ } ,
2344+ } ;
2345+
22512346// Export modules and make them available as globals for require()
22522347exposeCustomGlobal ( "_httpModule" , http ) ;
22532348exposeCustomGlobal ( "_httpsModule" , https ) ;
22542349exposeCustomGlobal ( "_http2Module" , http2 ) ;
22552350exposeCustomGlobal ( "_dnsModule" , dns ) ;
22562351exposeCustomGlobal ( "_netModule" , net ) ;
2352+ exposeCustomGlobal ( "_tlsModule" , tlsModule ) ;
22572353exposeCustomGlobal ( "_httpServerDispatch" , dispatchServerRequest ) ;
22582354exposeCustomGlobal ( "_httpServerUpgradeDispatch" , dispatchUpgradeRequest ) ;
22592355exposeCustomGlobal ( "_upgradeSocketData" , onUpgradeSocketData ) ;
@@ -2280,6 +2376,7 @@ export default {
22802376 https,
22812377 http2,
22822378 net,
2379+ tls : tlsModule ,
22832380 IncomingMessage,
22842381 ClientRequest,
22852382} ;
0 commit comments