[web3.js] Eliminate dependency on `URL` class (#27349)
* fix: `makeWebsocketUrl` no longer depends on the `URL` class * fix: `Connection` no longer relies on the `URL` class * fix: remove dependency on `react-native-url-polyfill`
This commit is contained in:
parent
b8b3d723da
commit
5975176af7
File diff suppressed because it is too large
Load Diff
|
@ -69,7 +69,6 @@
|
||||||
"jayson": "^3.4.4",
|
"jayson": "^3.4.4",
|
||||||
"js-sha3": "^0.8.0",
|
"js-sha3": "^0.8.0",
|
||||||
"node-fetch": "2",
|
"node-fetch": "2",
|
||||||
"react-native-url-polyfill": "^1.3.0",
|
|
||||||
"rpc-websockets": "^7.5.0",
|
"rpc-websockets": "^7.5.0",
|
||||||
"secp256k1": "^4.0.2",
|
"secp256k1": "^4.0.2",
|
||||||
"superstruct": "^0.14.2",
|
"superstruct": "^0.14.2",
|
||||||
|
|
|
@ -43,7 +43,6 @@ import {
|
||||||
TransactionExpiredTimeoutError,
|
TransactionExpiredTimeoutError,
|
||||||
} from './transaction/expiry-custom-errors';
|
} from './transaction/expiry-custom-errors';
|
||||||
import {makeWebsocketUrl} from './utils/makeWebsocketUrl';
|
import {makeWebsocketUrl} from './utils/makeWebsocketUrl';
|
||||||
import {URL} from './utils/url-impl';
|
|
||||||
import type {Blockhash} from './blockhash';
|
import type {Blockhash} from './blockhash';
|
||||||
import type {FeeCalculator} from './fee-calculator';
|
import type {FeeCalculator} from './fee-calculator';
|
||||||
import type {TransactionSignature} from './transaction';
|
import type {TransactionSignature} from './transaction';
|
||||||
|
@ -305,6 +304,14 @@ export type BlockheightBasedTransactionConfirmationStrategy = {
|
||||||
signature: TransactionSignature;
|
signature: TransactionSignature;
|
||||||
} & BlockhashWithExpiryBlockHeight;
|
} & BlockhashWithExpiryBlockHeight;
|
||||||
|
|
||||||
|
/* @internal */
|
||||||
|
function assertEndpointUrl(putativeUrl: string) {
|
||||||
|
if (/^https?:/.test(putativeUrl) === false) {
|
||||||
|
throw new TypeError('Endpoint URL must start with `http:` or `https:`.');
|
||||||
|
}
|
||||||
|
return putativeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
function extractCommitmentFromConfig<TConfig>(
|
function extractCommitmentFromConfig<TConfig>(
|
||||||
commitmentOrConfig?: Commitment | ({commitment?: Commitment} & TConfig),
|
commitmentOrConfig?: Commitment | ({commitment?: Commitment} & TConfig),
|
||||||
|
@ -1117,7 +1124,6 @@ export type PerfSample = {
|
||||||
|
|
||||||
function createRpcClient(
|
function createRpcClient(
|
||||||
url: string,
|
url: string,
|
||||||
useHttps: boolean,
|
|
||||||
httpHeaders?: HttpHeaders,
|
httpHeaders?: HttpHeaders,
|
||||||
customFetch?: FetchFn,
|
customFetch?: FetchFn,
|
||||||
fetchMiddleware?: FetchMiddleware,
|
fetchMiddleware?: FetchMiddleware,
|
||||||
|
@ -1126,7 +1132,7 @@ function createRpcClient(
|
||||||
const fetch = customFetch ? customFetch : fetchImpl;
|
const fetch = customFetch ? customFetch : fetchImpl;
|
||||||
let agentManager: AgentManager | undefined;
|
let agentManager: AgentManager | undefined;
|
||||||
if (!process.env.BROWSER) {
|
if (!process.env.BROWSER) {
|
||||||
agentManager = new AgentManager(useHttps);
|
agentManager = new AgentManager(url.startsWith('https:') /* useHttps */);
|
||||||
}
|
}
|
||||||
|
|
||||||
let fetchWithMiddleware: FetchFn | undefined;
|
let fetchWithMiddleware: FetchFn | undefined;
|
||||||
|
@ -2493,9 +2499,6 @@ export class Connection {
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
commitmentOrConfig?: Commitment | ConnectionConfig,
|
commitmentOrConfig?: Commitment | ConnectionConfig,
|
||||||
) {
|
) {
|
||||||
let url = new URL(endpoint);
|
|
||||||
const useHttps = url.protocol === 'https:';
|
|
||||||
|
|
||||||
let wsEndpoint;
|
let wsEndpoint;
|
||||||
let httpHeaders;
|
let httpHeaders;
|
||||||
let fetch;
|
let fetch;
|
||||||
|
@ -2514,12 +2517,11 @@ export class Connection {
|
||||||
disableRetryOnRateLimit = commitmentOrConfig.disableRetryOnRateLimit;
|
disableRetryOnRateLimit = commitmentOrConfig.disableRetryOnRateLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._rpcEndpoint = endpoint;
|
this._rpcEndpoint = assertEndpointUrl(endpoint);
|
||||||
this._rpcWsEndpoint = wsEndpoint || makeWebsocketUrl(endpoint);
|
this._rpcWsEndpoint = wsEndpoint || makeWebsocketUrl(endpoint);
|
||||||
|
|
||||||
this._rpcClient = createRpcClient(
|
this._rpcClient = createRpcClient(
|
||||||
url.toString(),
|
endpoint,
|
||||||
useHttps,
|
|
||||||
httpHeaders,
|
httpHeaders,
|
||||||
fetch,
|
fetch,
|
||||||
fetchMiddleware,
|
fetchMiddleware,
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
export {default} from 'react-native-url-polyfill';
|
|
||||||
export * from 'react-native-url-polyfill';
|
|
|
@ -1,20 +1,26 @@
|
||||||
import {URL} from './url-impl';
|
const URL_RE = /^[^:]+:\/\/([^:[]+|\[[^\]]+\])(:\d+)?(.*)/i;
|
||||||
|
|
||||||
export function makeWebsocketUrl(endpoint: string) {
|
export function makeWebsocketUrl(endpoint: string) {
|
||||||
let url = new URL(endpoint);
|
const matches = endpoint.match(URL_RE);
|
||||||
const useHttps = url.protocol === 'https:';
|
if (matches == null) {
|
||||||
|
throw TypeError(`Failed to validate endpoint URL \`${endpoint}\``);
|
||||||
url.protocol = useHttps ? 'wss:' : 'ws:';
|
}
|
||||||
url.host = '';
|
const [
|
||||||
|
_, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
|
hostish,
|
||||||
|
portWithColon,
|
||||||
|
rest,
|
||||||
|
] = matches;
|
||||||
|
const protocol = endpoint.startsWith('https:') ? 'wss:' : 'ws:';
|
||||||
|
const startPort =
|
||||||
|
portWithColon == null ? null : parseInt(portWithColon.slice(1), 10);
|
||||||
|
const websocketPort =
|
||||||
// Only shift the port by +1 as a convention for ws(s) only if given endpoint
|
// Only shift the port by +1 as a convention for ws(s) only if given endpoint
|
||||||
// is explictly specifying the endpoint port (HTTP-based RPC), assuming
|
// is explictly specifying the endpoint port (HTTP-based RPC), assuming
|
||||||
// we're directly trying to connect to solana-validator's ws listening port.
|
// we're directly trying to connect to solana-validator's ws listening port.
|
||||||
// When the endpoint omits the port, we're connecting to the protocol
|
// When the endpoint omits the port, we're connecting to the protocol
|
||||||
// default ports: http(80) or https(443) and it's assumed we're behind a reverse
|
// default ports: http(80) or https(443) and it's assumed we're behind a reverse
|
||||||
// proxy which manages WebSocket upgrade and backend port redirection.
|
// proxy which manages WebSocket upgrade and backend port redirection.
|
||||||
if (url.port !== '') {
|
startPort == null ? '' : `:${startPort + 1}`;
|
||||||
url.port = String(Number(url.port) + 1);
|
return `${protocol}//${hostish}${websocketPort}${rest}`;
|
||||||
}
|
|
||||||
return url.toString();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
export const URL = globalThis.URL;
|
|
||||||
export const URLSearchParams = globalThis.URLSearchParams;
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import {expect} from 'chai';
|
||||||
|
|
||||||
|
import {makeWebsocketUrl} from '../src/utils/makeWebsocketUrl';
|
||||||
|
|
||||||
|
const INVALID_URLS = [
|
||||||
|
'',
|
||||||
|
'0.0.0.0',
|
||||||
|
'localhost',
|
||||||
|
'www.no-protocol.com',
|
||||||
|
'//api.protocol.relative.com',
|
||||||
|
];
|
||||||
|
const TEST_CASES = [
|
||||||
|
// Non-https => `ws`
|
||||||
|
['http://api.devnet.solana.com/', 'ws://api.devnet.solana.com/'],
|
||||||
|
['gopher://gopher.example.com/', 'ws://gopher.example.com/'],
|
||||||
|
['http://localhost/', 'ws://localhost/'],
|
||||||
|
// `https` => `wss`
|
||||||
|
['https://api.devnet.solana.com/', 'wss://api.devnet.solana.com/'],
|
||||||
|
// IPv4 address
|
||||||
|
['https://192.168.0.1/', 'wss://192.168.0.1/'],
|
||||||
|
// IPv6 address
|
||||||
|
['https://[0:0:0:0:0:0:0:0]/', 'wss://[0:0:0:0:0:0:0:0]/'],
|
||||||
|
['https://[::]/', 'wss://[::]/'],
|
||||||
|
['https://[::1]/', 'wss://[::1]/'],
|
||||||
|
// Increment port if supplied
|
||||||
|
['https://api.devnet.solana.com:80/', 'wss://api.devnet.solana.com:81/'],
|
||||||
|
['https://192.168.0.1:443/', 'wss://192.168.0.1:444/'],
|
||||||
|
['https://[::]:8080/', 'wss://[::]:8081/'],
|
||||||
|
// No trailing slash
|
||||||
|
['http://api.devnet.solana.com', 'ws://api.devnet.solana.com'],
|
||||||
|
['https://api.devnet.solana.com', 'wss://api.devnet.solana.com'],
|
||||||
|
['https://api.devnet.solana.com:80', 'wss://api.devnet.solana.com:81'],
|
||||||
|
// Username
|
||||||
|
['https://alice@private.com', 'wss://alice@private.com'],
|
||||||
|
// Username/password
|
||||||
|
['https://bob:password@private.com', 'wss://bob:password@private.com'],
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('makeWebsocketUrl', () => {
|
||||||
|
TEST_CASES.forEach(([inputUrl, outputUrl]) => {
|
||||||
|
it(`converts \`${inputUrl}\` to \`${outputUrl}\``, () => {
|
||||||
|
expect(makeWebsocketUrl(inputUrl)).to.equal(outputUrl);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
INVALID_URLS.forEach(invalidUrl => {
|
||||||
|
it(`fatals when called with invalid url \`${invalidUrl}\``, () => {
|
||||||
|
expect(() => {
|
||||||
|
makeWebsocketUrl(invalidUrl);
|
||||||
|
}).to.throw();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
12942
web3.js/yarn.lock
12942
web3.js/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue