websocket client: remove the remaining unsafe from the module
Merged
JA
3mo ago+1190+1190+0123456789012345678901234567890123456789-1306-1306-0123456789012345678901234567890123456789
What
Follow-up to #40002 / #40018. Takes the whole websocket client module to zero unsafe blocks by fixing the layers underneath instead of annotating call sites:
| file | main | this PR |
|---|---|---|
websocket_client.rs | 71 | 0 |
WebSocketUpgradeClient.rs | 53 | 0 |
WebSocketProxyTunnel.rs | 15 | 0 |
CppWebSocket.rs | 10 | 2* |
WebSocketProxy.rs | 1 | 0 |
* the unsafe extern "C" { block keyword for the C++ imports (all now safe fn) and one comment.
- 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 throughUpgradeClientRef.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_jscand 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 anopenhandler has already closed the client. TheWebSocket__*imports aresafe fns over a new by-valuebun_core::ffi::FfiSlice<'a, T>({ptr,len}carrying the borrow's lifetime). - Initial-data microtask.
JSGlobalObject::queue_microtask_boxed<T: MicrotaskCallback>owns the one raw trampoline; the task and the client hold detachable back-references to each other; 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-1sentinel),Socket::take_ext_owner,SSL::servername(),VirtualMachine::{default_client_ssl_ctx, ssl_ctx_cache_get_or_create}(the twoRuntimeHooksentries become safe fns returningOwnedSslCtx),RefPtr::from_this,BackRef<_, Mut>::this_ptr.
Testing
Debug+ASAN: all 22 test/js/web/websocket/*.test.ts files (178 tests incl. proxy, proxy re-entrancy/leak, permessage-deflate, autobahn) pass; hand-driven ws/wss echo, wss verify-rejection, close-during-connect / during-handshake, non-101 response; BUN_DEBUG_alloc=1 shows 50/50 new/destroy for both HTTPClient and WebSocket across 50 connect/echo/close cycles.