feat: support custom request headers for WsProvider - #752
Open
sinzii wants to merge 2 commits into
Open
Conversation
Add a `headers` option to `WsProviderOptions` so custom HTTP headers (e.g: an auth token for a private RPC endpoint or proxy) can be sent along with the websocket opening handshake. Headers can be provided either as a static map or as a function that is resolved on every connection attempt (including reconnects), which is helpful to refresh short-lived tokens or to use different credentials per endpoint. The headers are passed as an options object in the second constructor argument, which is supported by both the `ws` package (used by `@polkadot/x-ws` on Node.js < 22) and the native WebSocket implementation (Node.js >= 22, Bun). Browsers and Deno follow the WHATWG spec where the second argument is a list of subprotocols, so headers are ignored there with a warning. Closes #751 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cpx2P4VETQHZ3hXGphLUAy
`@polkadot/x-ws` only ships a websocket client, so the previous test relied on the `ws` package for a server. Use a minimal handshake-only server built on `node:http` instead, keeping the test dependency-free while still exercising the real websocket implementation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cpx2P4VETQHZ3hXGphLUAy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #751
Adds a
headersoption toWsProviderOptionsso custom HTTP headers (e.g: an auth token for a private RPC endpoint or proxy) can be sent along with the websocket opening handshake.Headers can also be resolved on each connection attempt (including reconnects), which is helpful to refresh short-lived tokens or to use different credentials per endpoint. The selector receives the same
WsConnectionStateas the endpoint selector, so the endpoint about to be connected to is available:No dependency changes — the websocket is still created through
@polkadot/x-wsas before, andyarn.lockis untouched.Why the headers are passed as the second constructor argument
@polkadot/x-wsresolves itsWebSocketexport viaextractGlobal('WebSocket', ws), so the underlying implementation depends on the runtime:wspackageWebSocket, since a globalWebSocketis definedPassing the options as a third argument (
new WebSocket(url, undefined, { headers })) only works for thewspackage and silently drops the headers on Node.js >= 22. Both implementations do accept an options object as the second constructor argument, which is what this PR uses — verified against both implementations, the header reaches the server and no bogusSec-WebSocket-Protocolis sent.Browsers and Deno follow the WHATWG spec where the second argument is a list of subprotocols, so headers cannot be sent there. In those environments the headers are ignored and a warning is logged once per provider instance (the headers selector is not invoked at all, to avoid a pointless token fetch). This is checked by a new
canSendRequestHeaders()helper. Headers are never logged.When no headers are configured, the websocket is constructed exactly as before.
Tests
WsProvider.spec.ts: the@polkadot/x-wsmock now records constructor arguments. New cases cover: no options passed when no headers are configured (guards the existing behavior), static headers, the selector receiving the connection state, headers being re-resolved on reconnection, an empty headers map being ignored, and headers being dropped with a warning when the environment does not support them.WsProviderHeaders.spec.ts(new): runs against a real websocket handshake without mocking the websocket implementation (// @vitest-environment node), asserting the headers actually arrive over the wire. This is what verifies the argument shape against whichever implementation the runtime picks — CI runs Node 18 (ws), locally verified on Node 22 (native). The server is a minimal handshake-onlynode:httpserver, so no extra dependency is needed (@polkadot/x-wsonly ships a client). Cross-checked by mutation: switching the implementation to the third-argument form makes this test fail on Node 22, which is exactly the regression it guards.utils.spec.ts: cases forcanSendRequestHeaderson Node.js, Bun, Deno and browsers.Docs are covered by the
WsProviderJSDoc examples and a section inpackages/providers/README.md; the user-facing docs site would need a matching follow-up.