Skip to content

Undici WebSocket only uses undocumented onUpgrade() handler, not documented onRequestUpgrade(). #4771

@domenic

Description

@domenic

Bug Description

I am trying to write a custom dispatcher and get all undici code to go through it. I am trying to do so following the docs at https://undici.nodejs.org/#/docs/api/Dispatcher?id=parameter-dispatchhandler .

However, my onRequestUpgrade callback is never called. Instead, the undocumented onUpgrade callback is called.

Reproducible By

Minimal example:

"use strict";
const { WebSocket, Agent } = require("undici");

const agent = new Agent();

new WebSocket("wss://echo.websocket.org/", {
  dispatcher: {
    dispatch(opts, handler) {
      console.log("Handler uses old API (onUpgrade):", "onUpgrade" in handler);
      console.log("Handler uses new API (onRequestUpgrade):", "onRequestUpgrade" in handler);
      return agent.dispatch(opts, handler);
    }
  }
});
More legitimate example with a non-minimal dispatcher
"use strict";
const { WebSocket, Agent } = require("undici");

// A minimal dispatcher that only supports the new handler API
class NewAPIOnlyDispatcher {
  #agent = new Agent();

  dispatch(opts, handler) {
    console.log("dispatch() called with handler keys:", Object.keys(handler));

    // Check which API the handler uses
    const hasOldAPI = "onUpgrade" in handler;
    const hasNewAPI = "onRequestUpgrade" in handler;

    console.log("Handler uses old API (onUpgrade):", hasOldAPI);
    console.log("Handler uses new API (onRequestUpgrade):", hasNewAPI);

    if (hasOldAPI && !hasNewAPI) {
      console.log("\n❌ BUG: WebSocket handler uses old API, not new API\n");
    }

    // Forward to real agent
    return this.#agent.dispatch(opts, handler);
  }

  close() {
    return this.#agent.close();
  }
}

const dispatcher = new NewAPIOnlyDispatcher();

// Need a real WebSocket server - use a public echo server
const ws = new WebSocket("wss://echo.websocket.org/", { dispatcher });

ws.addEventListener("open", () => {
  console.log("WebSocket opened");
  ws.close();
  dispatcher.close();
  process.exit(0);
});

ws.addEventListener("error", (e) => {
  console.log("WebSocket error:", e.message || e);
  dispatcher.close();
});

// Timeout in case nothing happens
setTimeout(() => {
  console.log("Timeout - closing");
  dispatcher.close();
  process.exit(1);
}, 5000);

Expected Behavior

I should be able to write a custom dispatcher and have undici use it, without relying on undocumented hooks.

Environment

Using undici main branch 44e437d (a bit ahead of v7.19.1), Node v25.3.0.

Additional context

In #4753 (comment) I was told not to rely on the old handler API existing, so I'm unsure how to proceed here.

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