[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",
|
||||
"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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
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}`;
|
||||
}
|
||||
|
|
|
@ -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