websocket not running in prod but works locally

websocket not running in prod but works locally
  • ⁠So there is a backend (in rust) endpoint which requires an api key for connection in header…
  • ⁠⁠Since in browser you can’t past header for ws, we are trying to connect localhost ws (browser/client) with nextJS-server by passing api key in url params from client and then nextJs endpoint to rust by passing headers •⁠ ⁠⁠everything locally works fine, but if from localhost I try to connect rust backend, it is throwing 400 error

Does this makes sense? Here is the psudo code

import type { Server as HTTPServer } from "http";
import type { NextApiRequest, NextApiResponse } from "next";
import { Server as IOServer } from "socket.io";
import WebSocket from "ws"; 

import { getApiUrl } from "@/lib/utils";

type NextApiResponseWithSocket = NextApiResponse & {
  socket: {
    server: HTTPServer & {
      io?: IOServer;
    };
  };
};

export default function handler(
  req: NextApiRequest,
  res: NextApiResponseWithSocket,
) {
  const { token } = req.query as {
    token: string;
  };

  console.log("Token:", token);
  // Extract datasetName directly from the path parameter
  const datasetName = req.query.datasetName as string;

  if (res.socket.server.io) {
    console.log("Socket is already running");
  } else {
    console.log("Socket is initializing");
    const io = new IOServer(res.socket.server);
    res.socket.server.io = io;

    io.on("connection", (socket) => {
      const apiUrl = getApiUrl();

      const wsProtocol = apiUrl.startsWith("https") ? "wss" : "ws";
      const wsUrl = `${wsProtocol}://${new URL(apiUrl).host}/dataset/entity_ws?name=${datasetName}`;
      console.log("wsUrl", wsUrl);

      const socketRust = new WebSocket(wsUrl, {
        headers: {
          api_key: token,
        },
      });
      socketRust.onopen = () => {
        console.log("[WEBSOCKET] WebSocket connection established!");
        socket.broadcast.emit("update", {
          message: "connect success",
        });
      };
      socketRust.onerror = (event: any) => {
        console.log("Error", JSON.stringify(event));
        socket.broadcast.emit("update", {
          error: "something went wrong",
        });
      };
      socketRust.on("message", (data) => {
        console.log("Data", data);
        socket.broadcast.emit("update", data.toString());
      });

      socket.on("disconnect", () => {
        console.log("Disconnecting");
        socketRust.close();
      });
    });
  }

  res.end();
}

I am not sure how to go about this, so might be missing some important code but this is websocket test code

let dataset_name = "person";
    let url = format!(
        "ws://localhost:8080/dataset/entity_ws?name={}",
        dataset_name
    );

    println!(
        "{}",
        format!("[WEBSOCKET] Connecting to WebSocket at URL: {}", url).blue()
    );

    let mut request = tokio_tungstenite::tungstenite::http::Request::builder()
        .uri(url)
        .header("api_key", token)
        .body(())
        .expect("Failed to build request");

    // Add required WebSocket headers
    let key = tokio_tungstenite::tungstenite::handshake::client::generate_key();
    request
        .headers_mut()
        .insert("Sec-WebSocket-Key", key.parse().unwrap());
    request
        .headers_mut()
        .insert("Sec-WebSocket-Version", "13".parse().unwrap());
    request
        .headers_mut()
        .insert("Connection", "Upgrade".parse().unwrap());
    request
        .headers_mut()
        .insert("Upgrade", "websocket".parse().unwrap());
    request
        .headers_mut()
        .insert("Host", "localhost:8080".parse().unwrap());

    let (mut ws_stream, _) = connect_async(request)
        .await
        .expect("Failed to connect to WebSocket");

    println!("{}", "[WEBSOCKET] WebSocket connection established!".blue());

    // Listen to the websocket
    tokio::spawn(async move {
        println!(
            "{}",
            "[WEBSOCKET] Listening for WebSocket messages...".blue()
        );
    ```

Answer

The issue is likely in how you're establishing the WebSocket connection to the Rust backend. When using ws in Node.js environment (your Next.js API route), the headers need to be passed differently than what you have in your code.

  1. In your Next.js handler, you're creating a WebSocket connection like this:

const socketRust = new WebSocket(wsUrl, { headers: { api_key: token, }, });

But the ws package in Node.js expects headers to be passed differently when used on the server side.

Try modifying your code like this:

const socketRust = new WebSocket(wsUrl, { headers: { 'API-Key': token, // Make sure case matches what your Rust server expects } });

If that doesn't work, another approach is to use the request option:

const socketRust = new WebSocket(wsUrl, { rejectUnauthorized: false, // Only for development! headers: { 'API-Key': token, } });

Also, check if your Rust backend expects the header to be "api_key" or "API-Key"

Enjoyed this article?

Check out more content on our blog or follow us on social media.

Browse more articles