Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

fix: use the native fetch API in browsers and a patched node-fetch otherwise #25576

Merged
merged 3 commits into from
May 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,436 changes: 921 additions & 515 deletions web3.js/package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions web3.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@
"borsh": "^0.7.0",
"bs58": "^4.0.1",
"buffer": "6.0.1",
"cross-fetch": "^3.1.4",
"fast-stable-stringify": "^1.0.0",
"jayson": "^3.4.4",
"js-sha3": "^0.8.0",
"node-fetch": "2",
"rpc-websockets": "^7.4.2",
"secp256k1": "^4.0.2",
"superstruct": "^0.14.2",
Expand All @@ -82,7 +82,7 @@
"@babel/register": "^7.12.13",
"@commitlint/config-conventional": "^15.0.0",
"@commitlint/travis-cli": "^17.0.0",
"@rollup/plugin-alias": "^3.1.2",
"@rollup/plugin-alias": "^3.1.9",
"@rollup/plugin-babel": "^5.2.3",
"@rollup/plugin-commonjs": "^22.0.0",
"@rollup/plugin-json": "^4.1.0",
Expand All @@ -98,6 +98,7 @@
"@types/mocha": "^9.0.0",
"@types/mz": "^2.7.3",
"@types/node": "^17.0.24",
"@types/node-fetch": "2",
"@types/secp256k1": "^4.0.1",
"@types/sinon": "^10.0.0",
"@types/sinon-chai": "^3.2.8",
Expand Down Expand Up @@ -138,4 +139,4 @@
"engines": {
"node": ">=12.20.0"
}
}
}
43 changes: 41 additions & 2 deletions web3.js/rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import alias from '@rollup/plugin-alias';
import babel from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import * as fs from 'fs';
import json from '@rollup/plugin-json';
import path from 'path';
import nodeResolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import {terser} from 'rollup-plugin-terser';
Expand All @@ -15,6 +18,41 @@ function generateConfig(configType, format) {
const config = {
input: 'src/index.ts',
plugins: [
alias({
entries: [
{
find: /^\./, // Relative paths.
replacement: '.',
async customResolver(source, importer, options) {
const resolved = await this.resolve(source, importer, {
skipSelf: true,
...options,
});
if (resolved == null) {
return;
}
const {id: resolvedId} = resolved;
const directory = path.dirname(resolvedId);
const moduleFilename = path.basename(resolvedId);
const forkPath = path.join(
directory,
'__forks__',
configType,
moduleFilename,
);
const hasForkCacheKey = `has_fork:${forkPath}`;
let hasFork = this.cache.get(hasForkCacheKey);
if (hasFork === undefined) {
hasFork = fs.existsSync(forkPath);
this.cache.set(hasForkCacheKey, hasFork);
}
if (hasFork) {
return forkPath;
}
},
},
],
}),
commonjs(),
nodeResolve({
browser,
Expand Down Expand Up @@ -63,7 +101,7 @@ function generateConfig(configType, format) {
'crypto-hash',
'jayson/lib/client/browser',
'js-sha3',
'cross-fetch',
'node-fetch',
'rpc-websockets',
'secp256k1',
'superstruct',
Expand All @@ -75,7 +113,7 @@ function generateConfig(configType, format) {
case 'browser':
switch (format) {
case 'iife': {
config.external = ['http', 'https'];
config.external = ['http', 'https', 'node-fetch'];

config.output = [
{
Expand Down Expand Up @@ -123,6 +161,7 @@ function generateConfig(configType, format) {
'https',
'jayson/lib/client/browser',
'js-sha3',
'node-fetch',
'rpc-websockets',
'secp256k1',
'superstruct',
Expand Down
4 changes: 4 additions & 0 deletions web3.js/src/__forks__/browser/fetch-impl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const Headers = globalThis.Headers;
export const Request = globalThis.Request;
export const Response = globalThis.Response;
export default globalThis.fetch;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

globalThis will resolve to self in workers, and window in browsers. Both have fetch available on them.

31 changes: 17 additions & 14 deletions web3.js/src/connection.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import bs58 from 'bs58';
import {Buffer} from 'buffer';
import crossFetch from 'cross-fetch';
// @ts-ignore
import fastStableStringify from 'fast-stable-stringify';
import {
Expand Down Expand Up @@ -28,6 +27,7 @@ import RpcClient from 'jayson/lib/client/browser';
import {AgentManager} from './agent-manager';
import {EpochSchedule} from './epoch-schedule';
import {SendTransactionError} from './errors';
import fetchImpl, {Response} from './fetch-impl';
import {NonceAccount} from './nonce-account';
import {PublicKey} from './publickey';
import {Signer} from './keypair';
Expand Down Expand Up @@ -955,27 +955,25 @@ function createRpcClient(
url: string,
useHttps: boolean,
httpHeaders?: HttpHeaders,
customFetch?: typeof crossFetch,
customFetch?: FetchFn,
fetchMiddleware?: FetchMiddleware,
disableRetryOnRateLimit?: boolean,
): RpcClient {
const fetch = customFetch ? customFetch : crossFetch;
const fetch = customFetch ? customFetch : fetchImpl;
let agentManager: AgentManager | undefined;
if (!process.env.BROWSER) {
agentManager = new AgentManager(useHttps);
}

let fetchWithMiddleware:
| ((url: string, options: any) => Promise<Response>)
| undefined;
let fetchWithMiddleware: FetchFn | undefined;

if (fetchMiddleware) {
fetchWithMiddleware = async (url: string, options: any) => {
const modifiedFetchArgs = await new Promise<[string, any]>(
fetchWithMiddleware = async (info, init) => {
const modifiedFetchArgs = await new Promise<Parameters<FetchFn>>(
(resolve, reject) => {
try {
fetchMiddleware(url, options, (modifiedUrl, modifiedOptions) =>
resolve([modifiedUrl, modifiedOptions]),
fetchMiddleware(info, init, (modifiedInfo, modifiedInit) =>
resolve([modifiedInfo, modifiedInit]),
);
} catch (error) {
reject(error);
Expand Down Expand Up @@ -2162,13 +2160,18 @@ export type ConfirmedSignatureInfo = {
*/
export type HttpHeaders = {[header: string]: string};

/**
* The type of the JavaScript `fetch()` API
*/
export type FetchFn = typeof fetchImpl;

/**
* A callback used to augment the outgoing HTTP request
*/
export type FetchMiddleware = (
url: string,
options: any,
fetch: (modifiedUrl: string, modifiedOptions: any) => void,
info: Parameters<FetchFn>[0],
init: Parameters<FetchFn>[1],
fetch: (...a: Parameters<FetchFn>) => void,
) => void;

/**
Expand All @@ -2182,7 +2185,7 @@ export type ConnectionConfig = {
/** Optional HTTP headers object */
httpHeaders?: HttpHeaders;
/** Optional custom fetch function */
fetch?: typeof crossFetch;
fetch?: FetchFn;
/** Optional fetch middleware callback */
fetchMiddleware?: FetchMiddleware;
/** Optional Disable retrying calls when server responds with HTTP 429 (Too Many Requests) */
Expand Down
13 changes: 13 additions & 0 deletions web3.js/src/fetch-impl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as nodeFetch from 'node-fetch';

export * from 'node-fetch';
export default async function (
input: nodeFetch.RequestInfo,
init?: nodeFetch.RequestInit,
): Promise<nodeFetch.Response> {
const processedInput =
typeof input === 'string' && input.slice(0, 2) === '//'
Copy link
Contributor

Choose a reason for hiding this comment

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

when would the input be missing a protocol and just start with //?

Copy link
Contributor Author

@steveluscher steveluscher May 26, 2022

Choose a reason for hiding this comment

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

This is a standard patch for the broken implementation of node-fetch that doesn't support protocol-relative URLs like the fetch in browsers does. https://github.com/node-fetch/node-fetch/blob/main/docs/v2-LIMITS.md

Folks can and will write apps that make use of protocol-relative URLs, and we don't want them to break in Node when they do.

This replicates what cross-fetch was already doing.

https://github.com/lquixada/cross-fetch/blob/24dedb4c8a16a33cb9b4d4682fb731438a6a9e2d/src/node-ponyfill.js#L4-L11

? 'https:' + input
: input;
return await nodeFetch.default(processedInput, init);
}
1 change: 1 addition & 0 deletions web3.js/typedoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"entryPoints": ["src/index.ts"],
"excludeInternal": true,
"excludePrivate": true,
"intentionallyNotExported": ["src/fetch-impl.ts:default"],
"out": "doc"
}
32 changes: 26 additions & 6 deletions web3.js/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1521,9 +1521,10 @@
dependencies:
"@octokit/openapi-types" "^11.2.0"

"@rollup/plugin-alias@^3.1.2":
"@rollup/plugin-alias@^3.1.9":
version "3.1.9"
resolved "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-3.1.9.tgz"
resolved "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-3.1.9.tgz#a5d267548fe48441f34be8323fb64d1d4a1b3fdf"
integrity sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==
dependencies:
slash "^3.0.0"

Expand Down Expand Up @@ -1851,6 +1852,14 @@
dependencies:
"@types/node" "*"

"@types/node-fetch@2":
version "2.6.1"
resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975"
integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==
dependencies:
"@types/node" "*"
form-data "^3.0.0"

"@types/node@*", "@types/node@>=12", "@types/node@^17.0.24":
version "17.0.35"
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.35.tgz#635b7586086d51fb40de0a2ec9d1014a5283ba4a"
Expand Down Expand Up @@ -2744,9 +2753,10 @@ columnify@~1.5.4:
strip-ansi "^3.0.0"
wcwidth "^1.0.0"

combined-stream@^1.0.6, combined-stream@~1.0.6:
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"

Expand Down Expand Up @@ -3757,6 +3767,15 @@ forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"

form-data@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"

form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz"
Expand Down Expand Up @@ -5453,9 +5472,10 @@ node-emoji@^1.10.0:
dependencies:
lodash "^4.17.21"

node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@~2.6.1:
node-fetch@2, node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@~2.6.1:
version "2.6.7"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"

Expand Down