Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[web3.js] Eliminate dependency on URL class #27349

Merged
merged 3 commits into from
Aug 24, 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
41,166 changes: 15,328 additions & 25,838 deletions web3.js/package-lock.json

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion web3.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
20 changes: 11 additions & 9 deletions web3.js/src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -1117,7 +1124,6 @@ export type PerfSample = {

function createRpcClient(
url: string,
useHttps: boolean,
httpHeaders?: HttpHeaders,
customFetch?: FetchFn,
fetchMiddleware?: FetchMiddleware,
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand Down
2 changes: 0 additions & 2 deletions web3.js/src/utils/__forks__/react-native/url-impl.ts

This file was deleted.

38 changes: 22 additions & 16 deletions web3.js/src/utils/makeWebsocketUrl.ts
Original file line number Diff line number Diff line change
@@ -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}`;
}
2 changes: 0 additions & 2 deletions web3.js/src/utils/url-impl.ts

This file was deleted.

52 changes: 52 additions & 0 deletions web3.js/test/makeWebsocketUrl.test.ts
Original file line number Diff line number Diff line change
@@ -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();
});
});
});
Loading