workers-sdk icon indicating copy to clipboard operation
workers-sdk copied to clipboard

🐛 BUG: `wrangler dev` does not propagate WebSocket close `code` and `reason`

Open mrbbot opened this issue 3 years ago • 0 comments

What version of Wrangler are you using?

2.0.29

What operating system are you using?

Mac

Describe the Bug

When returning a WebSocket Response from a worker, wrangler dev does not correctly proxy the close code and reason from either the worker to the client, or the client to the worker. Instead it always defaults to a code of 1000 and an empty reason.

Example: Closing from Worker

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    if (url.pathname === "/") {
      return new Response(
        `<!DOCTYPE html>
        <html>
        <body>
        <script>
        const url = new URL("/ws", window.origin);
        url.protocol = url.protocol.replace("http", "ws");
        const webSocket = new WebSocket(url);
        webSocket.addEventListener("open", () => {
          console.log("open");
        });
        webSocket.addEventListener("close", (event) => {
          console.log("close", event.code, event.reason);
        });
        </script>
        </body>
        </html>
        `,
        { headers: { "Content-Type": "text/html; charset=utf-8" } }
      );
    } else if (url.pathname === "/ws") {
      const [client, worker] = Object.values(new WebSocketPair());
      worker.accept();
      ctx.waitUntil(
        scheduler.wait(3000).then(() => worker.close(3001, "Worker Close"))
      );
      return new Response(null, { status: 101, webSocket: client });
    } else {
      return new Response(null, { status: 404 });
    }
  },
};

Running wrangler dev, visiting http://localhost:8787, logs open in the browser, then close 1000 <empty string> 3 seconds later. Publishing the same worker logs close 3001 Worker Close as expected.

Example: Closing from Client

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    if (url.pathname === "/") {
      return new Response(
        `<!DOCTYPE html>
        <html>
        <body>
        <script>
        const url = new URL("/ws", window.origin);
        url.protocol = url.protocol.replace("http", "ws");
        const webSocket = new WebSocket(url);
        webSocket.addEventListener("open", () => {
          console.log("open");
          setTimeout(() => {
            webSocket.close(3002, "Client Close");
          }, 3000);
        });
        </script>
        </body>
        </html>
        `,
        { headers: { "Content-Type": "text/html; charset=utf-8" } }
      );
    } else if (url.pathname === "/ws") {
      const [client, worker] = Object.values(new WebSocketPair());
      worker.accept();

      let closeResolve;
      const closePromise = new Promise((resolve) => (closeResolve = resolve));
      worker.addEventListener("close", (event) => {
        console.log(event.code, event.reason);
        closeResolve();
      });
      ctx.waitUntil(closePromise);

      return new Response(null, { status: 101, webSocket: client });
    } else {
      return new Response(null, { status: 404 });
    }
  },
};

Running wrangler dev, visiting http://localhost:8787, logs 1000 in the terminal 3 seconds later. Publishing the same worker, then tailing, logs 3002 Client Close in the terminal instead as expected.

mrbbot avatar Sep 11 '22 13:09 mrbbot