summaryrefslogtreecommitdiff
path: root/cli/tests/unit/websocket_test.ts
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2023-04-14 22:17:39 +0530
committerGitHub <noreply@github.com>2023-04-14 22:17:39 +0530
commita4111442191fff300132259752e6d2d5613d1871 (patch)
treeea7cd7d3b897ace1ca29b1f0dd66e8e0d8b6a76a /cli/tests/unit/websocket_test.ts
parentd01340dab0d686c7f2d6c6922239f5fee0bc4cef (diff)
fix(ext/websocket): Avoid write deadlock that requires read_frame to complete (#18705)
Fixes https://github.com/denoland/deno/issues/18700 Timeline of the events that lead to the bug. 1. WebSocket handshake complete 2. Server on `read_frame` holding an AsyncRefCell borrow of the WebSocket stream. 3. Client sends a TXT frame after a some time 4. Server recieves the frame and goes back to `read_frame`. 5. After some time, Server starts a `write_frame` but `read_frame` is still holding a borrow! ^--- Locked. read_frame needs to complete so we can resume the write. This commit changes all writes to directly borrow the `fastwebsocket::WebSocket` resource under the assumption that it won't affect ongoing reads.
Diffstat (limited to 'cli/tests/unit/websocket_test.ts')
-rw-r--r--cli/tests/unit/websocket_test.ts46
1 files changed, 46 insertions, 0 deletions
diff --git a/cli/tests/unit/websocket_test.ts b/cli/tests/unit/websocket_test.ts
index 6737edc11..948e2add2 100644
--- a/cli/tests/unit/websocket_test.ts
+++ b/cli/tests/unit/websocket_test.ts
@@ -36,3 +36,49 @@ Deno.test(async function websocketPingPong() {
await promise;
ws.close();
});
+
+// https://github.com/denoland/deno/issues/18700
+Deno.test(
+ { sanitizeOps: false, sanitizeResources: false },
+ async function websocketWriteLock() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+
+ const server = Deno.serve({
+ handler: (req) => {
+ const { socket, response } = Deno.upgradeWebSocket(req);
+ socket.onopen = function () {
+ setTimeout(() => socket.send("Hello"), 500);
+ };
+ socket.onmessage = function (e) {
+ assertEquals(e.data, "Hello");
+ ac.abort();
+ };
+ return response;
+ },
+ signal: ac.signal,
+ onListen: () => listeningPromise.resolve(),
+ hostname: "localhost",
+ port: 4246,
+ });
+
+ await listeningPromise;
+ const promise = deferred();
+ const ws = new WebSocket("ws://localhost:4246/");
+ assertEquals(ws.url, "ws://localhost:4246/");
+ ws.onerror = () => fail();
+ ws.onmessage = (e) => {
+ assertEquals(e.data, "Hello");
+ setTimeout(() => {
+ ws.send(e.data);
+ }, 1000);
+ promise.resolve();
+ };
+ ws.onclose = () => {
+ promise.resolve();
+ };
+
+ await Promise.all([promise, server]);
+ ws.close();
+ },
+);