Skip to content

fix(websocket): don't require Sec-WebSocket-Accept on an h2 handshake - #5774

Open
kjsik11 wants to merge 1 commit into
nodejs:mainfrom
kjsik11:fix/websocket-h2-no-accept
Open

fix(websocket): don't require Sec-WebSocket-Accept on an h2 handshake#5774
kjsik11 wants to merge 1 commit into
nodejs:mainfrom
kjsik11:fix/websocket-h2-no-accept

Conversation

@kjsik11

@kjsik11 kjsik11 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

This relates to...

WebSocket over h2 (RFC 8441) handshake validation.

Reported in #5681 and closed as a server-compliance issue — but per RFC 8441 §5 the response is valid: Sec-WebSocket-Accept is not processed on an h2 handshake.

Rationale

Over h1, the handshake response is validated against RFC 6455 §4.1 steps 2–6.
Over h2, Sec-WebSocket-Key and Sec-WebSocket-Accept are not processed at all — RFC 8441 §5:

Implementations using this extended CONNECT to bootstrap WebSockets do not do the processing of the Sec-WebSocket-Key and Sec-WebSocket-Accept header fields of [RFC6455] as that functionality has been superseded by the :protocol pseudo-header field.

undici already skips steps 2 (Upgrade) and 3 (Connection) for h2, but not step 4 (Sec-WebSocket-Accept).
An h2 server that responds without Sec-WebSocket-Accept therefore fails step 4 and closes with 1006.

Repro(.mjs) — 1006 on main, opens with this PR
import { readFileSync } from "node:fs";
import { once } from "node:events";
import { createSecureServer } from "node:http2";
import { WebSocket, Agent } from "./index.js";

const server = createSecureServer({
  key: readFileSync("./test/fixtures/key.pem"),
  cert: readFileSync("./test/fixtures/cert.pem"),
  settings: { enableConnectProtocol: true },
});
server.on("stream", (stream) => {
  stream.respond({ ":status": 200 }); // no Sec-WebSocket-Accept
  stream.on("error", () => {});
});
await once(server.listen(0), "listening");

const dispatcher = new Agent({
  allowH2: true,
  connect: { rejectUnauthorized: false },
});
const ws = new WebSocket(`wss://localhost:${server.address().port}`, {
  dispatcher,
});
const stop = () => {
  ws.onerror = ws.onclose = null;
  dispatcher.destroy();
  server.close();
};
ws.onopen = () => {
  console.log("open");
  stop();
};
ws.onclose = ({ code }) => {
  console.log("close:", code);
  stop();
};

Changes

  • lib/web/websocket/connection.js: give step 4 the same h2 gate steps 2 and 3 have.
  • test/websocket/opening-handshake.js: an h2 server responds 200 without Sec-WebSocket-Accept and the test awaits open. Fails on main, passes with the patch.

Features

N/A

Bug Fixes

  • WebSocket over h2 now connects to servers that follow RFC 8441 and omit Sec-WebSocket-Accept.

Breaking Changes and Deprecations

N/A

Test results (Node 24.20.0)

  • npm run lint clean
  • test/websocket/**/*.js 148/148

Assisted-by: Claude Code

Status

@codecov-commenter

codecov-commenter commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.53%. Comparing base (eb04cc3) to head (f5be374).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5774      +/-   ##
==========================================
+ Coverage   93.51%   93.53%   +0.01%     
==========================================
  Files         110      110              
  Lines       39324    39325       +1     
==========================================
+ Hits        36774    36781       +7     
+ Misses       2550     2544       -6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants