Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a8c01ac
ws: support upgrade and unexpected-response events
May 30, 2026
d88e35c
ws: addEventListener returns undefined; drop explicit test timeouts
May 30, 2026
fc51f8c
[autofix.ci] apply automated fixes
autofix-ci[bot] May 30, 2026
287d4c7
ws: dispatch non-101 chunked responses immediately; Buffer.alloc in test
May 30, 2026
8870e90
ws: drop vestigial self-alias guard; don't leak noopBridgeListener
May 30, 2026
785a88d
ws: coalesce handshake response headers like Node IncomingMessage
May 30, 2026
14dc45c
ws: dispatch bodiless non-101 handshake responses immediately
robobun May 30, 2026
c4bd364
[autofix.ci] apply automated fixes
autofix-ci[bot] May 30, 2026
83c2d50
ci: retrigger
robobun May 30, 2026
84f8d5e
ws: correct SAFETY/doc comments on handshake header-slice lifetime
robobun May 30, 2026
c2912f7
ws: fix use-after-free when terminate() is called in 'unexpected-resp…
robobun May 30, 2026
872f259
[autofix.ci] apply automated fixes
autofix-ci[bot] May 30, 2026
abc1a91
test: drop unused stderr destructure from RST-UAF regression test
robobun May 30, 2026
cdb7b42
ws: suppress native error after 'unexpected-response' for addEventLis…
robobun May 30, 2026
02e85bb
[autofix.ci] apply automated fixes
autofix-ci[bot] May 30, 2026
66d76cb
ws: only suppress native error when unexpected-response fired; dedupe…
robobun May 30, 2026
25bd646
[autofix.ci] apply automated fixes
autofix-ci[bot] May 30, 2026
e26d51c
ws: document the ext-slot-take unsafe block in handle_close (clippy)
robobun May 30, 2026
7c68a6d
ws: don't double-emit 'error' for non-101 with on('error') but no une…
robobun May 30, 2026
5edeb2c
[autofix.ci] apply automated fixes
autofix-ci[bot] May 30, 2026
1a32241
ws: fix 'error' suppression when on() and addEventListener/onerror ar…
robobun May 30, 2026
a784b3c
[autofix.ci] apply automated fixes
autofix-ci[bot] May 30, 2026
f49a485
ws: fire 'upgrade' and 'open' under one event-loop scope on 101
robobun May 30, 2026
0b89041
ws: dedup addEventListener('upgrade'/'unexpected-response') and clear…
robobun May 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/http_jsc/websocket_client/CppWebSocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ bun_opaque::opaque_ffi! {
pub struct CppWebSocket;
}

/// A single HTTP response header passed across the FFI to
/// `WebSocket__didReceiveHandshakeResponse`. Name/value point into the
/// PicoHTTP-parsed response head, which the caller keeps alive for the
/// duration of the synchronous dispatch. Matches `WebCore::WebSocket::
/// HandshakeRawHeader` (src/jsc/bindings/webcore/WebSocket.h).
#[repr(C)]
pub struct RawHeader {
pub name_ptr: *const u8,
pub name_len: usize,
pub value_ptr: *const u8,
pub value_len: usize,
}

// FFI surface for `WebCore::WebSocket` (src/jsc/bindings/webcore/WebSocket.cpp).
// Kept private to this module — the safe wrappers below are the only callers.
//
Expand All @@ -45,6 +58,16 @@ unsafe extern "C" {
deflate_params: *const websocket_deflate::Params,
);
safe fn WebSocket__didAbruptClose(websocket_context: &CppWebSocket, reason: ErrorCode);
fn WebSocket__didReceiveHandshakeResponse(
websocket_context: &CppWebSocket,
status_code: u16,
status_message: *const u8,
status_message_len: usize,
headers: *const RawHeader,
headers_len: usize,
body: *const u8,
body_len: usize,
);
fn WebSocket__didClose(websocket_context: &CppWebSocket, code: u16, reason: *const BunString);
fn WebSocket__didReceiveText(
websocket_context: &CppWebSocket,
Expand Down Expand Up @@ -78,6 +101,39 @@ impl CppWebSocket {
event_loop.exit();
}

/// Dispatch the native `'handshake'` event to the `ws` shim.
///
/// `status_message`, `headers`, and `body` are borrowed for the call only
/// — the C++ side copies whatever it needs into JS values synchronously.
// Forwards raw pointers to C++ without dereferencing on the Rust side.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub(crate) fn did_receive_handshake_response(
&self,
status_code: u16,
status_message: &[u8],
headers: &[RawHeader],
body: &[u8],
) {
// SAFETY: VirtualMachine::get() returns the live current-thread VM;
// event_loop() yields its raw event-loop pointer (live for VM lifetime).
let event_loop = VirtualMachine::get().event_loop_mut();
event_loop.enter();
// SAFETY: self is a valid C++ WebCore::WebSocket; the slices outlive the call.
unsafe {
WebSocket__didReceiveHandshakeResponse(
self,
status_code,
status_message.as_ptr(),
status_message.len(),
headers.as_ptr(),
headers.len(),
body.as_ptr(),
body.len(),
)
};
event_loop.exit();
Comment thread
robobun marked this conversation as resolved.
}

pub(crate) fn did_close(&self, code: u16, reason: &mut BunString) {
// SAFETY: VirtualMachine::get() returns the live current-thread VM;
// event_loop() yields its raw event-loop pointer (live for VM lifetime).
Expand Down
530 changes: 496 additions & 34 deletions src/http_jsc/websocket_client/WebSocketUpgradeClient.rs

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/js/builtins/BunBuiltinNames.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ using namespace JSC;
macro(pullAlgorithm) \
macro(pulling) \
macro(queue) \
macro(rawHeaders) \
macro(read) \
macro(readIntoRequests) \
macro(readRequests) \
Expand Down Expand Up @@ -193,6 +194,8 @@ using namespace JSC;
macro(started) \
macro(state) \
macro(status) \
macro(statusCode) \
macro(statusMessage) \
macro(statusText) \
macro(storedError) \
macro(strategy) \
Expand Down
Loading
Loading