mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00
lib: add fetch
Fixes: https://github.com/nodejs/node/issues/19393 PR-URL: https://github.com/nodejs/node/pull/41749 Refs: https://github.com/nodejs/undici/pull/1183 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Mestery <mestery@protonmail.com> Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Myles Borins <myles.borins@gmail.com>
This commit is contained in:
parent
7123a00b03
commit
6ec2253926
16 changed files with 8076 additions and 3 deletions
|
@ -338,5 +338,9 @@ module.exports = {
|
|||
atob: 'readable',
|
||||
performance: 'readable',
|
||||
structuredClone: 'readable',
|
||||
fetch: 'readable',
|
||||
Headers: 'readable',
|
||||
Request: 'readable',
|
||||
Response: 'readable',
|
||||
},
|
||||
};
|
||||
|
|
25
LICENSE
25
LICENSE
|
@ -634,6 +634,31 @@ The externally maintained libraries used by Node.js are:
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- undici, located at deps/undici, is licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) Matteo Collina and Undici contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- OpenSSL, located at deps/openssl, is licensed as follows:
|
||||
"""
|
||||
Apache License
|
||||
|
|
21
deps/undici/LICENSE
vendored
Normal file
21
deps/undici/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Matteo Collina and Undici contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
7887
deps/undici/undici.js
vendored
Normal file
7887
deps/undici/undici.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -280,6 +280,14 @@ effort to report stack traces relative to the original source file.
|
|||
Overriding `Error.prepareStackTrace` prevents `--enable-source-maps` from
|
||||
modifying the stack trace.
|
||||
|
||||
### `--experimental-fetch`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Enable experimental support for the [Fetch API][].
|
||||
|
||||
### `--experimental-import-meta-resolve`
|
||||
|
||||
<!-- YAML
|
||||
|
@ -1559,6 +1567,7 @@ Node.js options that are allowed are:
|
|||
* `--enable-fips`
|
||||
* `--enable-source-maps`
|
||||
* `--experimental-abortcontroller`
|
||||
* `--experimental-fetch`
|
||||
* `--experimental-import-meta-resolve`
|
||||
* `--experimental-json-modules`
|
||||
* `--experimental-loader`
|
||||
|
@ -1952,6 +1961,7 @@ $ node --max-old-space-size=1536 index.js
|
|||
[Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/
|
||||
[CommonJS]: modules.md
|
||||
[ECMAScript module loader]: esm.md#loaders
|
||||
[Fetch API]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
|
||||
[Modules loaders]: packages.md#modules-loaders
|
||||
[OSSL_PROVIDER-legacy]: https://www.openssl.org/docs/man3.0/man7/OSSL_PROVIDER-legacy.html
|
||||
[REPL]: repl.md
|
||||
|
|
|
@ -333,6 +333,17 @@ A browser-compatible implementation of the `EventTarget` class. See
|
|||
|
||||
This variable may appear to be global but is not. See [`exports`][].
|
||||
|
||||
## `fetch`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1 - Experimental. Enable this API with the [`--experimental-fetch`][]
|
||||
> CLI flag.
|
||||
|
||||
A browser-compatible implementation of the [`fetch()`][] function.
|
||||
|
||||
## `global`
|
||||
|
||||
<!-- YAML
|
||||
|
@ -348,6 +359,17 @@ within the browser `var something` will define a new global variable. In
|
|||
Node.js this is different. The top-level scope is not the global scope;
|
||||
`var something` inside a Node.js module will be local to that module.
|
||||
|
||||
## Class `Headers`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1 - Experimental. Enable this API with the [`--experimental-fetch`][]
|
||||
> CLI flag.
|
||||
|
||||
A browser-compatible implementation of {Headers}.
|
||||
|
||||
## `MessageChannel`
|
||||
|
||||
<!-- YAML
|
||||
|
@ -442,6 +464,28 @@ DataHandler.prototype.load = async function load(key) {
|
|||
|
||||
This variable may appear to be global but is not. See [`require()`][].
|
||||
|
||||
## `Response`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1 - Experimental. Enable this API with the [`--experimental-fetch`][]
|
||||
> CLI flag.
|
||||
|
||||
A browser-compatible implementation of {Response}.
|
||||
|
||||
## `Request`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1 - Experimental. Enable this API with the [`--experimental-fetch`][]
|
||||
> CLI flag.
|
||||
|
||||
A browser-compatible implementation of {Request}.
|
||||
|
||||
## `setImmediate(callback[, ...args])`
|
||||
|
||||
<!-- YAML
|
||||
|
@ -546,6 +590,7 @@ The object that acts as the namespace for all W3C
|
|||
[WebAssembly][webassembly-org] related functionality. See the
|
||||
[Mozilla Developer Network][webassembly-mdn] for usage and compatibility.
|
||||
|
||||
[`--experimental-fetch`]: cli.md#--experimental-fetch
|
||||
[`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
|
||||
[`DOMException`]: https://developer.mozilla.org/en-US/docs/Web/API/DOMException
|
||||
[`EventTarget` and `Event` API]: events.md#eventtarget-and-event-api
|
||||
|
@ -565,6 +610,7 @@ The object that acts as the namespace for all W3C
|
|||
[`clearTimeout`]: timers.md#cleartimeouttimeout
|
||||
[`console`]: console.md
|
||||
[`exports`]: modules.md#exports
|
||||
[`fetch()`]: https://developer.mozilla.org/en-US/docs/Web/API/fetch
|
||||
[`module`]: modules.md#module
|
||||
[`perf_hooks.performance`]: perf_hooks.md#perf_hooksperformance
|
||||
[`process.nextTick()`]: process.md#processnexttickcallback-args
|
||||
|
|
|
@ -139,6 +139,9 @@ Requires Node.js to be built with
|
|||
.It Fl -enable-source-maps
|
||||
Enable Source Map V3 support for stack traces.
|
||||
.
|
||||
.It Fl -experimental-fetch
|
||||
Enable experimental support for the Fetch API.
|
||||
.
|
||||
.It Fl -experimental-import-meta-resolve
|
||||
Enable experimental ES modules support for import.meta.resolve().
|
||||
.
|
||||
|
|
|
@ -14,6 +14,11 @@ const {
|
|||
getEmbedderOptions,
|
||||
} = require('internal/options');
|
||||
const { reconnectZeroFillToggle } = require('internal/buffer');
|
||||
const {
|
||||
defineOperation,
|
||||
emitExperimentalWarning,
|
||||
exposeInterface,
|
||||
} = require('internal/util');
|
||||
|
||||
const { Buffer } = require('buffer');
|
||||
const { ERR_MANIFEST_ASSERT_INTEGRITY } = require('internal/errors').codes;
|
||||
|
@ -30,6 +35,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
|
|||
setupPerfHooks();
|
||||
setupInspectorHooks();
|
||||
setupWarningHandler();
|
||||
setupFetch();
|
||||
|
||||
// Resolve the coverage directory to an absolute path, and
|
||||
// overwrite process.env so that the original path gets passed
|
||||
|
@ -139,6 +145,21 @@ function setupWarningHandler() {
|
|||
}
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/
|
||||
function setupFetch() {
|
||||
if (!getOptionValue('--experimental-fetch')) {
|
||||
return;
|
||||
}
|
||||
|
||||
emitExperimentalWarning('Fetch');
|
||||
|
||||
const undici = require('internal/deps/undici/undici');
|
||||
defineOperation(globalThis, 'fetch', undici.fetch);
|
||||
exposeInterface(globalThis, 'Headers', undici.Headers);
|
||||
exposeInterface(globalThis, 'Request', undici.Request);
|
||||
exposeInterface(globalThis, 'Response', undici.Response);
|
||||
}
|
||||
|
||||
// Setup User-facing NODE_V8_COVERAGE environment variable that writes
|
||||
// ScriptCoverage to a specified file.
|
||||
function setupCoverageHooks(dir) {
|
||||
|
@ -479,6 +500,7 @@ module.exports = {
|
|||
patchProcessObject,
|
||||
setupCoverageHooks,
|
||||
setupWarningHandler,
|
||||
setupFetch,
|
||||
setupDebugEnv,
|
||||
setupPerfHooks,
|
||||
prepareMainThreadExecution,
|
||||
|
|
|
@ -17,6 +17,7 @@ const {
|
|||
setupCoverageHooks,
|
||||
setupInspectorHooks,
|
||||
setupWarningHandler,
|
||||
setupFetch,
|
||||
setupDebugEnv,
|
||||
setupPerfHooks,
|
||||
initializeDeprecations,
|
||||
|
@ -67,6 +68,7 @@ setupInspectorHooks();
|
|||
setupDebugEnv();
|
||||
|
||||
setupWarningHandler();
|
||||
setupFetch();
|
||||
initializeSourceMapsHandlers();
|
||||
|
||||
// Since worker threads cannot switch cwd, we do not need to
|
||||
|
|
1
node.gyp
1
node.gyp
|
@ -51,6 +51,7 @@
|
|||
'deps/acorn/acorn-walk/dist/walk.js',
|
||||
'deps/cjs-module-lexer/lexer.js',
|
||||
'deps/cjs-module-lexer/dist/lexer.js',
|
||||
'deps/undici/undici.js',
|
||||
],
|
||||
'node_mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)node_mksnapshot<(EXECUTABLE_SUFFIX)',
|
||||
'mkcodecache_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)mkcodecache<(EXECUTABLE_SUFFIX)',
|
||||
|
|
|
@ -315,6 +315,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
|
|||
kAllowedInEnvironment);
|
||||
AddOption("--experimental-abortcontroller", "",
|
||||
NoOp{}, kAllowedInEnvironment);
|
||||
AddOption("--experimental-fetch",
|
||||
"experimental Fetch API",
|
||||
&EnvironmentOptions::experimental_fetch,
|
||||
kAllowedInEnvironment);
|
||||
AddOption("--experimental-json-modules", "", NoOp{}, kAllowedInEnvironment);
|
||||
AddOption("--experimental-loader",
|
||||
"use the specified module as a custom loader",
|
||||
|
|
|
@ -107,6 +107,7 @@ class EnvironmentOptions : public Options {
|
|||
std::vector<std::string> conditions;
|
||||
std::string dns_result_order;
|
||||
bool enable_source_maps = false;
|
||||
bool experimental_fetch = false;
|
||||
std::string experimental_specifier_resolution;
|
||||
bool experimental_wasm_modules = false;
|
||||
bool experimental_import_meta_resolve = false;
|
||||
|
|
|
@ -68,12 +68,12 @@ if (process.argv.length === 2 &&
|
|||
!process.env.NODE_SKIP_FLAG_CHECK &&
|
||||
isMainThread &&
|
||||
hasCrypto &&
|
||||
require.main &&
|
||||
require('cluster').isPrimary) {
|
||||
require('cluster').isPrimary &&
|
||||
fs.existsSync(process.argv[1])) {
|
||||
// The copyright notice is relatively big and the flags could come afterwards.
|
||||
const bytesToRead = 1500;
|
||||
const buffer = Buffer.allocUnsafe(bytesToRead);
|
||||
const fd = fs.openSync(require.main.filename, 'r');
|
||||
const fd = fs.openSync(process.argv[1], 'r');
|
||||
const bytesRead = fs.readSync(fd, buffer, 0, bytesToRead);
|
||||
fs.closeSync(fd);
|
||||
const source = buffer.toString('utf8', 0, bytesRead);
|
||||
|
@ -300,6 +300,15 @@ if (global.structuredClone) {
|
|||
knownGlobals.push(global.structuredClone);
|
||||
}
|
||||
|
||||
if (global.fetch) {
|
||||
knownGlobals.push(
|
||||
global.fetch,
|
||||
global.Request,
|
||||
global.Response,
|
||||
global.Headers,
|
||||
);
|
||||
}
|
||||
|
||||
function allowGlobals(...allowlist) {
|
||||
knownGlobals = knownGlobals.concat(allowlist);
|
||||
}
|
||||
|
|
32
test/parallel/test-fetch.mjs
Normal file
32
test/parallel/test-fetch.mjs
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Flags: --experimental-fetch --no-warnings
|
||||
|
||||
import '../common/index.mjs';
|
||||
|
||||
import assert from 'assert';
|
||||
import events from 'events';
|
||||
import http from 'http';
|
||||
|
||||
assert.strictEqual(typeof globalThis.fetch, 'function');
|
||||
assert.strictEqual(typeof globalThis.Headers, 'function');
|
||||
assert.strictEqual(typeof globalThis.Request, 'function');
|
||||
assert.strictEqual(typeof globalThis.Response, 'function');
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
// TODO: Remove this once keep-alive behavior can be disabled from the client
|
||||
// side.
|
||||
res.setHeader('Keep-Alive', 'timeout=0, max=0');
|
||||
res.end('Hello world');
|
||||
});
|
||||
server.listen(0);
|
||||
await events.once(server, 'listening');
|
||||
const port = server.address().port;
|
||||
|
||||
const response = await fetch(`http://localhost:${port}`);
|
||||
|
||||
assert(response instanceof Response);
|
||||
assert.strictEqual(response.status, 200);
|
||||
assert.strictEqual(response.statusText, 'OK');
|
||||
const body = await response.text();
|
||||
assert.strictEqual(body, 'Hello world');
|
||||
|
||||
server.close();
|
|
@ -270,6 +270,10 @@ const customTypesMap = {
|
|||
'webstreams.md#class-textencoderstream',
|
||||
'TextDecoderStream':
|
||||
'webstreams.md#class-textdecoderstream',
|
||||
|
||||
'Headers': 'https://developer.mozilla.org/en-US/docs/Web/API/Headers',
|
||||
'Response': 'https://developer.mozilla.org/en-US/docs/Web/API/Response',
|
||||
'Request': 'https://developer.mozilla.org/en-US/docs/Web/API/Request',
|
||||
};
|
||||
|
||||
const arrayPart = /(?:\[])+$/;
|
||||
|
|
|
@ -63,6 +63,8 @@ licenseText="$(cat deps/llhttp/LICENSE-MIT)"
|
|||
addlicense "llhttp" "deps/llhttp" "$licenseText"
|
||||
licenseText="$(cat "${rootdir}"/deps/corepack/LICENSE.md)"
|
||||
addlicense "corepack" "deps/corepack" "$licenseText"
|
||||
licenseText="$(cat "${rootdir}"/deps/undici/LICENSE)"
|
||||
addlicense "undici" "deps/undici" "$licenseText"
|
||||
licenseText="$(cat "${rootdir}"/deps/openssl/openssl/LICENSE.txt)"
|
||||
addlicense "OpenSSL" "deps/openssl" "$licenseText"
|
||||
licenseText="$(curl -sL https://raw.githubusercontent.com/bestiejs/punycode.js/HEAD/LICENSE-MIT.txt)"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue