[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:
Steven Luscher 2022-08-24 11:02:40 -07:00 committed by GitHub
parent b8b3d723da
commit 5975176af7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 7365 additions and 17758 deletions

12066
web3.js/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -69,7 +69,6 @@
"jayson": "^3.4.4",
"js-sha3": "^0.8.0",
"node-fetch": "2",
"react-native-url-polyfill": "^1.3.0",
"rpc-websockets": "^7.5.0",
"secp256k1": "^4.0.2",
"superstruct": "^0.14.2",

View File

@ -43,7 +43,6 @@ import {
TransactionExpiredTimeoutError,
} from './transaction/expiry-custom-errors';
import {makeWebsocketUrl} from './utils/makeWebsocketUrl';
import {URL} from './utils/url-impl';
import type {Blockhash} from './blockhash';
import type {FeeCalculator} from './fee-calculator';
import type {TransactionSignature} from './transaction';
@ -305,6 +304,14 @@ export type BlockheightBasedTransactionConfirmationStrategy = {
signature: TransactionSignature;
} & 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 */
function extractCommitmentFromConfig<TConfig>(
commitmentOrConfig?: Commitment | ({commitment?: Commitment} & TConfig),
@ -1117,7 +1124,6 @@ export type PerfSample = {
function createRpcClient(
url: string,
useHttps: boolean,
httpHeaders?: HttpHeaders,
customFetch?: FetchFn,
fetchMiddleware?: FetchMiddleware,
@ -1126,7 +1132,7 @@ function createRpcClient(
const fetch = customFetch ? customFetch : fetchImpl;
let agentManager: AgentManager | undefined;
if (!process.env.BROWSER) {
agentManager = new AgentManager(useHttps);
agentManager = new AgentManager(url.startsWith('https:') /* useHttps */);
}
let fetchWithMiddleware: FetchFn | undefined;
@ -2493,9 +2499,6 @@ export class Connection {
endpoint: string,
commitmentOrConfig?: Commitment | ConnectionConfig,
) {
let url = new URL(endpoint);
const useHttps = url.protocol === 'https:';
let wsEndpoint;
let httpHeaders;
let fetch;
@ -2514,12 +2517,11 @@ export class Connection {
disableRetryOnRateLimit = commitmentOrConfig.disableRetryOnRateLimit;
}
this._rpcEndpoint = endpoint;
this._rpcEndpoint = assertEndpointUrl(endpoint);
this._rpcWsEndpoint = wsEndpoint || makeWebsocketUrl(endpoint);
this._rpcClient = createRpcClient(
url.toString(),
useHttps,
endpoint,
httpHeaders,
fetch,
fetchMiddleware,

View File

@ -1,2 +0,0 @@
export {default} from 'react-native-url-polyfill';
export * from 'react-native-url-polyfill';

View File

@ -1,20 +1,26 @@
import {URL} from './url-impl';
const URL_RE = /^[^:]+:\/\/([^:[]+|\[[^\]]+\])(:\d+)?(.*)/i;
export function makeWebsocketUrl(endpoint: string) {
let url = new URL(endpoint);
const useHttps = url.protocol === 'https:';
url.protocol = useHttps ? 'wss:' : 'ws:';
url.host = '';
// 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
// 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
// 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.
if (url.port !== '') {
url.port = String(Number(url.port) + 1);
const matches = endpoint.match(URL_RE);
if (matches == null) {
throw TypeError(`Failed to validate endpoint URL \`${endpoint}\``);
}
return url.toString();
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
// is explictly specifying the endpoint port (HTTP-based RPC), assuming
// 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
// 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.
startPort == null ? '' : `:${startPort + 1}`;
return `${protocol}//${hostish}${websocketPort}${rest}`;
}

View File

@ -1,2 +0,0 @@
export const URL = globalThis.URL;
export const URLSearchParams = globalThis.URLSearchParams;

View File

@ -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();
});
});
});

File diff suppressed because it is too large Load Diff