diff --git a/src/js/thirdparty/ws.js b/src/js/thirdparty/ws.js index 25db515009..731d381c14 100644 --- a/src/js/thirdparty/ws.js +++ b/src/js/thirdparty/ws.js @@ -700,6 +700,23 @@ class BunWebSocketMocked extends EventEmitter { this.#ws.close(code, reason); } } + + terminate() { + let state = this.#state; + if (state === 3) return; + if (state === 0) { + const msg = "WebSocket was closed before the connection was established"; + abortHandshake(this, this._req, msg); + return; + } + + let ws = this.#ws; + if (ws) { + this.#state = WebSocket.CLOSING; + ws.terminate(); + } + } + get binaryType() { return this.#binaryType; } diff --git a/test/js/first_party/ws/ws.test.ts b/test/js/first_party/ws/ws.test.ts index 58468b5a6f..abc031bb7d 100644 --- a/test/js/first_party/ws/ws.test.ts +++ b/test/js/first_party/ws/ws.test.ts @@ -276,6 +276,25 @@ describe("WebSocketServer", () => { new WebSocket("ws://localhost:" + wss.address().port); await promise; }); + + it("sockets can be terminated", async () => { + const wss = new WebSocketServer({ port: 0 }); + const { resolve, reject, promise } = Promise.withResolvers(); + + wss.on("connection", ws => { + ws.on("close", () => { + resolve(); + }); + try { + ws.terminate(); + } catch (err) { + reject(err); + } + }); + + new WebSocket("ws://localhost:" + wss.address().port); + await promise; + }); }); describe("Server", () => {