Skip to content

Conversation

@mcollina
Copy link
Member

@mcollina mcollina commented Jan 17, 2026

Summary

  • Adds Authorization header from URL credentials when establishing WebSocket connections
  • Respects custom Authorization headers set via options.headers
  • Configures WPT test runner to trust the WPT server's CA certificate for TLS connections
  • Fixes close event timing when close() is called in CONNECTING state:
    • Events now fire asynchronously after close() returns (instead of synchronously during)
    • readyState now transitions correctly: CONNECTING → CLOSING → CLOSED

WPT Tests Now Passing

  • websockets/basic-auth.any.html?wss
  • websockets/interfaces/WebSocket/close/close-connecting-async.any.html (all variants)
  • websockets/interfaces/WebSocket/close/close-connecting.html (all variants)
  • websockets/interfaces/WebSocket/close/close-multiple.html (all variants)
  • websockets/interfaces/WebSocket/close/close-nested.html (all variants)

Fixes #4744
Fixes #4741
Fixes #4742

When creating a WebSocket connection with embedded credentials in the URL
(e.g., ws://foo:bar@localhost:1337/), the Authorization header was not
being generated. This aligns the behavior with the ws package and web
standards.

Fixes: #4744
@codecov-commenter
Copy link

codecov-commenter commented Jan 17, 2026

Codecov Report

❌ Patch coverage is 86.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.97%. Comparing base (250efc8) to head (4c1ec04).

Files with missing lines Patch % Lines
lib/web/websocket/websocket.js 60.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4745      +/-   ##
==========================================
+ Coverage   92.89%   92.97%   +0.08%     
==========================================
  Files         109      109              
  Lines       33886    33900      +14     
==========================================
+ Hits        31478    31519      +41     
+ Misses       2408     2381      -27     

☔ View full report in Codecov by Sentry.
📢 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.

The WPT server uses pregenerated self-signed certificates. Configure
the global dispatcher to trust the CA certificate so that wss:// and
https:// connections work properly in WPT tests.
…CTING state

When close() is called on a WebSocket in the CONNECTING state, events
were firing synchronously during close() instead of being queued, and
readyState was transitioning incorrectly (CONNECTING → CLOSED → CLOSING
instead of CONNECTING → CLOSING → CLOSED).

- Queue onSocketClose() via queueMicrotask in failWebsocketConnection()
- Add guard in #onSocketClose to prevent duplicate calls

Fixes #4741
Fixes #4742
@mcollina mcollina changed the title fix(websocket): add Authorization header from URL credentials fix(websocket): add Authorization header and fix close event timing Jan 17, 2026
Comment on lines +558 to +560
if (this.#handler.readyState === states.CLOSED) {
return
}
Copy link
Member

@KhafraDev KhafraDev Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From local testing, this only seems to be called once. If we kept this in we'd be hiding other bugs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't understand what you want to be done here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reference is actually wrong, I'll amend.

Comment on lines +55 to +61
// If the URL has credentials, add Authorization header (unless already set by options.headers)
// @see https://fetch.spec.whatwg.org/#concept-basic-fetch (step 12.2.3)
if ((url.username || url.password) && !request.headersList.contains('authorization', true)) {
const credentials = `${url.username}:${url.password}`
const encoded = Buffer.from(credentials).toString('base64')
request.headersList.append('authorization', `Basic ${encoded}`, true)
}
Copy link
Member

@KhafraDev KhafraDev Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my reasoning for why this should not be set: #4744 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

step 12.2.3

What is this referring to? scheme fetch doesn't have a 12.2.3

Comment on lines +558 to +560
if (this.#handler.readyState === states.CLOSED) {
return
}
Copy link
Member

@KhafraDev KhafraDev Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From local testing, this only seems to be called once. If we kept this in we'd be hiding other bugs.

@domenic
Copy link

domenic commented Jan 17, 2026

I'm concerned this fix isn't quite following the spec. It adds some extra state transition guards and a microtask. Whereas, the proper per-spec fix would be to add a real task (not microtask) at a higher level. E.g. instead of a microtask just for the handler.onSocketClose() part of failWebsocketConnection, the entirety establishWebSocketConnection, closeWebSocketConnection, and whatever undici's equivalent of "when a WebSocket message has been received" is.

I suspect if that sort of fix is implemented, then no specific state transition patches will be necessary, since the state transitions will always be queued properly in the task queue.

@mcollina mcollina closed this Jan 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

5 participants