Skip to content

Bun engine handshake missing headers bug #5394

@eSkaarAS

Description

@eSkaarAS

Describe the bug
I am using the new Bun engine as described here: https://socket.io/blog/bun-engine/
I am using it with hono, but i think that is irrelevant for this issue.

The problem is that i want to assosiate the connection with a session from the handshake. I might be doing this in a stupid way.
I am spinning up a Hono server, handling the socket.io connection request in the export and then when the client connection, the allowRequest function in the engine sees the headers and i can validate them, but then the headers are not present in the handshake in the socket middleware or in the socket.on("connection", ...) I have tried both.

To Reproduce

  • Spin up a hono server with bun (see code)
  • Do some auth (you need to have some headers)
  • Connect a websocket from a client (i am using react (include credentials))
  • Then look at the console.log from the allowRequest and the Connection and Middleware
  • Only the allowRequest log shows any headers on the handshake request

Socket.IO server version: 4.5.z

Server

import { Hono } from "hono";
import { auth } from "./lib/auth";
import { Server as Engine } from "@socket.io/bun-engine";
import { Server } from "socket.io";

const app = new Hono();
const io = new Server();

const engine = new Engine({
  path: "/[socket.io/](http://socket.io/)",
  cors: {
    origin: process.env.CORS_ORIGIN || "",
    methods: ["GET", "POST"],
  },
  allowRequest: async (req, server) => {
    console.log("Handshake headers:", req.headers); // <-- The headers are present
    const session = await auth.api.getSession({ headers: req.headers });
    console.log(session); // <-- This is a valid session
  },
});
io.bind(engine);

io.use(async (socket, next) => {
  const headers = socket.handshake.headers as unknown as Headers; // <-- The headers are NOT present
  const session = await auth.api.getSession({ headers });
  socket.data.session = session; // <-- This is NOT a valid session
  next();
});

io.on("connection", async (socket) => {
  console.log("New connection handshake headers:", socket.handshake);

  socket.on("disconnect", () => {
    console.log("Client disconnected");
  });
});

...
...
...

export default {
  port: 3000,
  idleTimeout: 30, // must be greater than the "pingInterval" option of the engine, which defaults to 25 seconds
  async fetch(req: Request, server: Bun.Server) {
    const url = new URL(req.url);

    if (url.pathname === "/socket.io/") {
      return engine.handleRequest(req, server);
    } else {
      return app.fetch(req, server);
    }
  },

  websocket,
};

Client

let socket: Socket | null = null;

function getBaseUrl() {
  return process.env.NODE_ENV === "production"
    ? window.location.origin
    : "http://localhost:3000";
}

function getSocket(): Socket {
  if (typeof window === "undefined") {
    throw new Error("getSocket() called during SSR");
  }
  if (!socket) {
    socket = io(getBaseUrl(), {
      withCredentials: true,
    });

    socket.on("connect", async () => {
      console.log("[ws] connected", socket?.id);
    });

    socket.on("disconnect", (reason) => {
      console.log("[ws] disconnected", reason);
    });
  }
  return socket;
}

Expected behavior
I expected the headers from the handshake to be present in the connection and middleware function

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions