websocket client: remove the remaining unsafe from the module
What
Follow-up to #40002. Takes the whole websocket client module — websocket_client.rs, WebSocketUpgradeClient.rs, WebSocketProxyTunnel.rs, WebSocketProxy.rs, CppWebSocket.rs — from 55 / 71 / 44 / 1 / 9 unsafe sites to zero (the one remaining unsafe keyword is the unsafe extern "C" { block declaring the C++ imports, all of which are now safe fn). Rather than annotate call sites, this fixes the layers underneath:
- Ownership. The refs held on behalf of the socket's userdata pointer and of C++'s
m_upgradeClient/m_connectedWebSocketareRefPtr<Self>fields, released withRefPtr::deref; no more hand-pairedSelf::deref(ptr). Customdeinitdestructors becomeDrop. - Back-references. Tunnel ↔ client links are
BackRef<_, Mut>built from the dispatch-timeThisPtrand cleared by the pointee before it can be freed; the tunnel dispatches into either transport viaUpgradeClientRef.SslWrapperhandlers are typedThisPtr<WebSocketProxyTunnel>(the wrapper was already generic over its ctx). - C++ boundary. Every
Bun__WebSocket*entry point is a plainHOST_EXPORTfn;generate-host-exports.tsnow scanssrc/http_jsc, and learns&[T](C passesptr, len) andThisPtr<T>params. C++ passes the header arrays as(ptr, len)pairs, the handshake-overflow bytes as one opaqueInitialDatabox, the customSSL_CTXasOption<OwnedSslCtx>, andsetConnectedWebSocketpassesnullptronce the open handler has closed the client. TheWebSocket__*imports aresafe fns over a new by-valuebun_core::ffi::FfiSlice<'a, T>({ptr,len}with the borrow's lifetime). - Initial-data microtask.
JSGlobalObject::queue_microtask_boxed<T: MicrotaskCallback>owns the trampoline; the task and the client hold detachable back-references to each other, and the C++ pending-activity ref is still released at VM shutdown if the queue never drains. - Small safe accessors replacing per-site raw handling:
Socket::ssl_mut()(andssl()no longer returns the connecting-socket sentinel),Socket::take_ext_owner,SSL::servername(),VirtualMachine::{default_client_ssl_ctx, ssl_ctx_cache_get_or_create}(theRuntimeHooksentries are now safe fns returningOwnedSslCtx),RefPtr::from_this,BackRef<_, Mut>::this_ptr.
Also from the earlier commit on this branch: WebSocketProxyTunnel::write/shutdown hold a ref across the SslWrapper call (its on_close can release the caller's ref mid-call).
Testing
Debug+ASAN: all 22 test/js/web/websocket/*.test.ts files (178 tests incl. proxy, proxy re-entrancy/leak, deflate, autobahn) pass; hand-driven ws/wss echo, wss verify-rejection, close-during-connect/handshake, non-101; BUN_DEBUG_alloc shows 50/50 new/destroy for both HTTPClient and WebSocket over 50 connections.