How to proxy websocket requests with workers
# workers-help
s
Hello Cloudflare Community! I'm currently trying to mask a website domainA.com to domainB.com with workers. (Client domainB.com (workers) domainA.com (Original Site)) The domainB.com is currently hooked with my workers. While I successfully proxied all web requests, the website domainA.com uses websocket to communicate with its backend server. Because my workers code only proxies web requests, if I access the site with domainB, all the web resources(CSS, JS, Imgs) loads well but it says that the websocket is not connected and the website throws an error. Here's my code.
Copy code
js
  addEventListener("fetch", async (event) => {
    let requestURL = new URL(event.request.url);
    let path = requestURL.pathname;
    let location = redirectMap.get(path);
    if (event.request.headers.get("upgrade") === "websocket") {
      const url = new URL(event.request.url);
      url.hostname = "domainA.com";
      const wsURL = `wss://${url.host}${url.pathname}${url.search}`;
      console.log(wsURL);
      const ws = new WebSocket(wsURL);
      const webSocketPair = new WebSocketPair();
      const [client, server] = Object.values(webSocketPair);
      server.accept();

      // Proxied data receiver
      ws.addEventListener('message', event => {
        console.log(`Original data received: ${event.data}`);
        server.send(event.data);
      })

      server.addEventListener('message', event => {
        console.log(`Received client data: ${event.data}`);
        ws.send(event.data);
      })

      ws.addEventListener('close', event => {
        ws.close();
      })

      server.addEventListener('close', event => {
        server.close();
        client.close();
      })

      return new Response(null, {
        status: 101,
        webSocket: client,
      });
    }
    else {
      if (location == void 0) {
        location = `https://domainA.com${path}`;
      }
  
      event.respondWith(fetch(location));
    }
  });
So I successfully filtered websocket requests by the code above. However, accessing the domainB.com will throw
failed to connect to the backend. Socket.io connect_error: websocket error
. What is the problem and how can I solve this issue?
i
Are you using Socket.IO on the client?
s
(I'm not 100% sure, but) Yes
m
Hey! đź‘‹ The callback function passed to
addEventListener()
must be synchronous (i.e. not marked
async
or return a
Promise
). Instead, I'd recommend you lift that function out into its own
async function handleEvent(event) {}
function which returns a
Promise<Response>
.
event.respondWith()
accepts either a
Response
or a
Promise<Response>
, so you can call your
handleEvent
function and pass the result directly to
respondWith()
. Something like...
Copy code
ts
async function handleEvent(event) {
  let requestURL = new URL(event.request.url);
  let path = requestURL.pathname;
  let location = redirectMap.get(path);
  if (event.request.headers.get("upgrade") === "websocket") {
    // ...
    return new Response(null, {
      status: 101,
      webSocket: client,
    });
  } else {
    // ...  
    return fetch(location);
  }
}

addEventListener("fetch", (event) => {
  event.respondWith(handleEvent(event));
});
s
Hi! Thank you for your answer! Lifting out the function fixed all my problem!