lib: add WebSocket client

fixup

add test

lint

fixup

update doc/node.1

PR-URL: https://github.com/nodejs/node/pull/49830
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Robert Nagy <ronagy@icloud.com>
This commit is contained in:
Khafra 2023-09-28 09:01:30 -04:00 committed by GitHub
parent a4fdb1abe0
commit e28dbe1c2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 72 additions and 20 deletions

View File

@ -361,5 +361,6 @@ module.exports = {
WritableStream: 'readable', WritableStream: 'readable',
WritableStreamDefaultWriter: 'readable', WritableStreamDefaultWriter: 'readable',
WritableStreamDefaultController: 'readable', WritableStreamDefaultController: 'readable',
WebSocket: 'readable',
}, },
}; };

View File

@ -738,6 +738,14 @@ added: v12.3.0
Enable experimental WebAssembly module support. Enable experimental WebAssembly module support.
### `--experimental-websocket`
<!-- YAML
added: REPLACEME
-->
Enable experimental [`WebSocket`][] support.
### `--force-context-aware` ### `--force-context-aware`
<!-- YAML <!-- YAML
@ -2248,6 +2256,7 @@ Node.js options that are allowed are:
* `--experimental-vm-modules` * `--experimental-vm-modules`
* `--experimental-wasi-unstable-preview1` * `--experimental-wasi-unstable-preview1`
* `--experimental-wasm-modules` * `--experimental-wasm-modules`
* `--experimental-websocket`
* `--force-context-aware` * `--force-context-aware`
* `--force-fips` * `--force-fips`
* `--force-node-api-uncaught-exceptions-policy` * `--force-node-api-uncaught-exceptions-policy`
@ -2715,6 +2724,7 @@ done
[`NODE_OPTIONS`]: #node_optionsoptions [`NODE_OPTIONS`]: #node_optionsoptions
[`NO_COLOR`]: https://no-color.org [`NO_COLOR`]: https://no-color.org
[`SlowBuffer`]: buffer.md#class-slowbuffer [`SlowBuffer`]: buffer.md#class-slowbuffer
[`WebSocket`]: https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
[`YoungGenerationSizeFromSemiSpaceSize`]: https://chromium.googlesource.com/v8/v8.git/+/refs/tags/10.3.129/src/heap/heap.cc#328 [`YoungGenerationSizeFromSemiSpaceSize`]: https://chromium.googlesource.com/v8/v8.git/+/refs/tags/10.3.129/src/heap/heap.cc#328
[`dns.lookup()`]: dns.md#dnslookuphostname-options-callback [`dns.lookup()`]: dns.md#dnslookuphostname-options-callback
[`dns.setDefaultResultOrder()`]: dns.md#dnssetdefaultresultorderorder [`dns.setDefaultResultOrder()`]: dns.md#dnssetdefaultresultorderorder

View File

@ -1018,6 +1018,17 @@ The object that acts as the namespace for all W3C
[WebAssembly][webassembly-org] related functionality. See the [WebAssembly][webassembly-org] related functionality. See the
[Mozilla Developer Network][webassembly-mdn] for usage and compatibility. [Mozilla Developer Network][webassembly-mdn] for usage and compatibility.
## `WebSocket`
<!-- YAML
added: REPLACEME
-->
> Stability: 1 - Experimental.
A browser-compatible implementation of [`WebSocket`][]. Enable this API
with the [`--experimental-websocket`][] CLI flag.
## Class: `WritableStream` ## Class: `WritableStream`
<!-- YAML <!-- YAML
@ -1052,6 +1063,7 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][].
[ECMAScript module]: esm.md [ECMAScript module]: esm.md
[Navigator API]: https://html.spec.whatwg.org/multipage/system-state.html#the-navigator-object [Navigator API]: https://html.spec.whatwg.org/multipage/system-state.html#the-navigator-object
[Web Crypto API]: webcrypto.md [Web Crypto API]: webcrypto.md
[`--experimental-websocket`]: cli.md#--experimental-websocket
[`--no-experimental-global-customevent`]: cli.md#--no-experimental-global-customevent [`--no-experimental-global-customevent`]: cli.md#--no-experimental-global-customevent
[`--no-experimental-global-webcrypto`]: cli.md#--no-experimental-global-webcrypto [`--no-experimental-global-webcrypto`]: cli.md#--no-experimental-global-webcrypto
[`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController [`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
@ -1085,6 +1097,7 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][].
[`TransformStream`]: webstreams.md#class-transformstream [`TransformStream`]: webstreams.md#class-transformstream
[`URLSearchParams`]: url.md#class-urlsearchparams [`URLSearchParams`]: url.md#class-urlsearchparams
[`URL`]: url.md#class-url [`URL`]: url.md#class-url
[`WebSocket`]: https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
[`WritableStreamDefaultController`]: webstreams.md#class-writablestreamdefaultcontroller [`WritableStreamDefaultController`]: webstreams.md#class-writablestreamdefaultcontroller
[`WritableStreamDefaultWriter`]: webstreams.md#class-writablestreamdefaultwriter [`WritableStreamDefaultWriter`]: webstreams.md#class-writablestreamdefaultwriter
[`WritableStream`]: webstreams.md#class-writablestream [`WritableStream`]: webstreams.md#class-writablestream

View File

@ -178,6 +178,9 @@ Use this flag to enable ShadowRealm support.
.It Fl -experimental-test-coverage .It Fl -experimental-test-coverage
Enable code coverage in the test runner. Enable code coverage in the test runner.
. .
.It Fl -experimental-websocket
Enable experimental support for the WebSocket API.
.
.It Fl -no-experimental-fetch .It Fl -no-experimental-fetch
Disable experimental support for the Fetch API. Disable experimental support for the Fetch API.
. .

View File

@ -78,7 +78,7 @@ function prepareExecution(options) {
setupTraceCategoryState(); setupTraceCategoryState();
setupInspectorHooks(); setupInspectorHooks();
setupWarningHandler(); setupWarningHandler();
setupFetch(); setupUndici();
setupWebCrypto(); setupWebCrypto();
setupCustomEvent(); setupCustomEvent();
setupCodeCoverage(); setupCodeCoverage();
@ -262,9 +262,9 @@ function setupWarningHandler() {
} }
// https://fetch.spec.whatwg.org/ // https://fetch.spec.whatwg.org/
function setupFetch() { // https://websockets.spec.whatwg.org/
if (getEmbedderOptions().noBrowserGlobals || function setupUndici() {
getOptionValue('--no-experimental-fetch')) { if (getEmbedderOptions().noBrowserGlobals) {
return; return;
} }
@ -278,12 +278,6 @@ function setupFetch() {
return undici; return undici;
} }
async function fetch(input, init = undefined) {
return lazyUndici().fetch(input, init);
}
defineOperation(globalThis, 'fetch', fetch);
function lazyInterface(name) { function lazyInterface(name) {
return { return {
configurable: true, configurable: true,
@ -297,17 +291,31 @@ function setupFetch() {
}; };
} }
ObjectDefineProperties(globalThis, { if (!getOptionValue('--no-experimental-fetch')) {
FormData: lazyInterface('FormData'), async function fetch(input, init = undefined) {
Headers: lazyInterface('Headers'), return lazyUndici().fetch(input, init);
Request: lazyInterface('Request'), }
Response: lazyInterface('Response'),
});
// The WebAssembly Web API: https://webassembly.github.io/spec/web-api defineOperation(globalThis, 'fetch', fetch);
internalBinding('wasm_web_api').setImplementation((streamState, source) => {
require('internal/wasm_web_api').wasmStreamingCallback(streamState, source); ObjectDefineProperties(globalThis, {
}); FormData: lazyInterface('FormData'),
Headers: lazyInterface('Headers'),
Request: lazyInterface('Request'),
Response: lazyInterface('Response'),
});
// The WebAssembly Web API: https://webassembly.github.io/spec/web-api
internalBinding('wasm_web_api').setImplementation((streamState, source) => {
require('internal/wasm_web_api').wasmStreamingCallback(streamState, source);
});
}
if (getOptionValue('--experimental-websocket')) {
ObjectDefineProperties(globalThis, {
WebSocket: lazyInterface('WebSocket'),
});
}
} }
// TODO(aduh95): move this to internal/bootstrap/web/* when the CLI flag is // TODO(aduh95): move this to internal/bootstrap/web/* when the CLI flag is

View File

@ -370,6 +370,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
&EnvironmentOptions::experimental_fetch, &EnvironmentOptions::experimental_fetch,
kAllowedInEnvvar, kAllowedInEnvvar,
true); true);
AddOption("--experimental-websocket",
"experimental WebSocket API",
&EnvironmentOptions::experimental_websocket,
kAllowedInEnvvar,
true);
AddOption("--experimental-global-customevent", AddOption("--experimental-global-customevent",
"expose experimental CustomEvent on the global scope", "expose experimental CustomEvent on the global scope",
&EnvironmentOptions::experimental_global_customevent, &EnvironmentOptions::experimental_global_customevent,

View File

@ -111,6 +111,7 @@ class EnvironmentOptions : public Options {
std::string dns_result_order; std::string dns_result_order;
bool enable_source_maps = false; bool enable_source_maps = false;
bool experimental_fetch = true; bool experimental_fetch = true;
bool experimental_websocket = false;
bool experimental_global_customevent = true; bool experimental_global_customevent = true;
bool experimental_global_web_crypto = true; bool experimental_global_web_crypto = true;
bool experimental_https_modules = false; bool experimental_https_modules = false;

View File

@ -123,6 +123,7 @@ const webIdlExposedWindow = new Set([
'Headers', 'Headers',
'Request', 'Request',
'Response', 'Response',
'WebSocket',
]); ]);
const nodeGlobals = new Set([ const nodeGlobals = new Set([

View File

@ -370,6 +370,9 @@ if (global.ReadableStream) {
global.DecompressionStream, global.DecompressionStream,
); );
} }
if (global.WebSocket) {
knownGlobals.push(WebSocket);
}
function allowGlobals(...allowlist) { function allowGlobals(...allowlist) {
knownGlobals = knownGlobals.concat(allowlist); knownGlobals = knownGlobals.concat(allowlist);

View File

@ -0,0 +1,7 @@
// Flags: --experimental-websocket
'use strict';
require('../common');
const assert = require('assert');
assert.strictEqual(typeof WebSocket, 'function');