Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
fix: make port 0 bind to any available port (#4070)
Browse files Browse the repository at this point in the history
Co-authored-by: Micaiah Reid <[email protected]>
  • Loading branch information
davidmurdoch and MicaiahReid authored Jan 31, 2023
1 parent 7ee7d4b commit a952ae7
Show file tree
Hide file tree
Showing 17 changed files with 137 additions and 94 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:

- name: Check bundle size
# this should match the os and version used in the release.yml
if: startsWith(matrix.os, 'ubuntu-22.04') && startsWith(matrix.node, '14.')
if: startsWith(matrix.os, 'ubuntu-22.04') && startsWith(matrix.node, '14.') && matrix.node != '14.0.0'
# 1. build ganache
# 2. pack it into a tarball
# 3. measure the _unpacked_ tarball's size
Expand All @@ -65,7 +65,7 @@ jobs:
INFURA_KEY: "badc0de0deadc0debadc0de0deadc0de"
- name: Upload artifact
# this should match the os and version used in the release.yml
if: startsWith(matrix.os, 'ubuntu-22.04') && startsWith(matrix.node, '14.')
if: startsWith(matrix.os, 'ubuntu-22.04') && startsWith(matrix.node, '14.') && matrix.node != '14.0.0'
uses: actions/upload-artifact@v3
with:
name: Candidate
Expand Down
6 changes: 3 additions & 3 deletions src/chains/ethereum/ethereum/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/chains/ethereum/ethereum/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
"ws": "8.2.3"
},
"devDependencies": {
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.1",
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.2",
"@types/encoding-down": "5.0.0",
"@types/fs-extra": "9.0.2",
"@types/keccak": "3.0.1",
Expand Down
24 changes: 3 additions & 21 deletions src/chains/filecoin/filecoin/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/chains/filecoin/filecoin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"@filecoin-shipyard/lotus-client-schema": "2.0.0",
"@ganache/filecoin-options": "0.7.0",
"@ganache/utils": "0.7.0",
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.1",
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.2",
"@types/abstract-leveldown": "7.2.0",
"@types/bn.js": "5.1.0",
"@types/deep-equal": "1.0.1",
Expand Down
12 changes: 6 additions & 6 deletions src/chains/tezos/tezos/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/chains/tezos/tezos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"emittery": "0.10.0"
},
"devDependencies": {
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.1",
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.2",
"@types/mocha": "9.0.0",
"cheerio": "1.0.0-rc.3",
"cross-env": "7.0.3",
Expand Down
2 changes: 1 addition & 1 deletion src/packages/cli/src/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export default function (
})
.check(argv => {
const { "server.port": port, "server.host": host } = argv;
if (port < 1 || port > 65535) {
if (port < 0 || port > 65535) {
throw new Error(`Invalid port number '${port}'`);
}

Expand Down
13 changes: 7 additions & 6 deletions src/packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,18 @@ if (argv.action === "start") {
return;
}
started = true;
const { address: host, port } = server.address();
switch (flavor) {
case FilecoinFlavorName: {
await initializeFilecoin(
server.provider as FilecoinProvider,
cliSettings
);
await initializeFilecoin(server.provider as FilecoinProvider, {
host,
port
});
break;
}
case EthereumFlavorName:
default: {
initializeEthereum(server.provider as EthereumProvider, cliSettings);
initializeEthereum(server.provider as EthereumProvider, { host, port });
break;
}
}
Expand All @@ -157,7 +158,7 @@ if (argv.action === "start") {
// instance), so we need to notify that we are ready.
const isDetachedInstance = process.send !== undefined;
if (isDetachedInstance) {
notifyDetachedInstanceReady();
notifyDetachedInstanceReady(port);
}
});
} else if (argv.action === "stop") {
Expand Down
21 changes: 11 additions & 10 deletions src/packages/cli/src/detach.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export type DetachedInstance = {
};

const FILE_ENCODING = "utf8";
const READY_MESSAGE = "ready";
const START_ERROR =
"An error occurred spawning a detached instance of Ganache:";
const dataPath = envPaths(`Ganache/instances`, { suffix: "" }).data;
Expand All @@ -32,10 +31,10 @@ function getInstanceFilePath(instanceName: string): string {
/**
* Notify that the detached instance has started and is ready to receive requests.
*/
export function notifyDetachedInstanceReady() {
// in "detach" mode, the parent will wait until the "ready" message is
export function notifyDetachedInstanceReady(port: number) {
// in "detach" mode, the parent will wait until the port is
// received before disconnecting from the child process.
process.send(READY_MESSAGE);
process.send(port);
}

/**
Expand Down Expand Up @@ -113,11 +112,13 @@ export async function startDetachedInstance(
// event is emitted) will be streamed to stderr on the parent.
child.stderr.pipe(process.stderr);

await new Promise<void>((resolve, reject) => {
child.on("message", message => {
if (message === READY_MESSAGE) {
resolve();
}
// Wait for the child process to send its port, which indicates that the
// Ganache server has started and is ready to receive RPC requests. It signals
// by sending the port number to which it was bound back to us; this is needed
// because Ganache may bind to a random port if the user specified port 0.
const port = await new Promise<number>((resolve, reject) => {
child.on("message", port => {
resolve(port as number);
});

child.on("error", err => {
Expand Down Expand Up @@ -146,7 +147,7 @@ export async function startDetachedInstance(
child.disconnect();

const flavor = instanceInfo.flavor;
const { host, port } = instanceInfo.server;
const { host } = instanceInfo.server;
const cmd =
process.platform === "win32"
? path.basename(process.execPath)
Expand Down
12 changes: 6 additions & 6 deletions src/packages/core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"@ganache/options": "0.7.0",
"@ganache/tezos": "0.7.0",
"@ganache/utils": "0.7.0",
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.1",
"@trufflesuite/uws-js-unofficial": "20.10.0-unofficial.2",
"aggregate-error": "3.1.0",
"emittery": "0.10.0"
},
Expand Down
40 changes: 31 additions & 9 deletions src/packages/core/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
import {
App,
us_listen_socket_close,
us_socket_local_port,
_cfg as setUwsGlobalConfig
} from "@trufflesuite/uws-js-unofficial";

Expand All @@ -33,6 +34,13 @@ import WebsocketServer, { WebSocketCapableFlavor } from "./servers/ws-server";
import HttpServer from "./servers/http-server";
import Emittery from "emittery";

// not using the "net" node package in order to avoid having to polyfill this
// for the browser build.
// isIPv4 taken from https://github.com/nodejs/node/blob/01323d50c4b24cf730a651d06ba20633905ecbed/lib/internal/net.js#L31
const v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])";
const IPv4Reg = new RegExp(`^(${v4Seg}[.]){3}${v4Seg}$`);
const isIPv4 = (s: string) => IPv4Reg.test(s);

export type Provider = Connector["provider"];

const DEFAULT_HOST = "127.0.0.1";
Expand All @@ -43,13 +51,13 @@ export type Callback = (err: Error | null) => void;
* Server ready state constants.
*
* These are bit flags. This means that you can check if the status is:
* * ready: `status === Status.ready` or `status & Status.ready !== 0`
* * opening: `status === Status.opening` or `status & Status.opening !== 0`
* * open: `status === Status.open` or `status & Status.open !== 0`
* * opening || open: `status & Status.openingOrOpen !== 0` or `status & (Status.opening | Status.open) !== 0`
* * closing: `status === Status.closing` or `status & Status.closing !== 0`
* * closed: `status === Status.closed` or `status & Status.closed !== 0`
* * closing || closed: `status & Status.closingOrClosed !== 0` or `status & (Status.closing | Status.closed) !== 0`
* * ready: `status === ServerStatus.ready` or `status & ServerStatus.ready !== 0`
* * opening: `status === ServerStatus.opening` or `status & ServerStatus.opening !== 0`
* * open: `status === ServerStatus.open` or `status & ServerStatus.open !== 0`
* * opening || open: `status & ServerStatus.openingOrOpen !== 0` or `status & (ServerStatus.opening | ServerStatus.open) !== 0`
* * closing: `status === ServerStatus.closing` or `status & ServerStatus.closing !== 0`
* * closed: `status === ServerStatus.closed` or `status & ServerStatus.closed !== 0`
* * closing || closed: `status & ServerStatus.closingOrClosed !== 0` or `status & (ServerStatus.closing | ServerStatus.closed) !== 0`
*/
export enum ServerStatus {
/**
Expand Down Expand Up @@ -106,6 +114,7 @@ export class Server<
#app: TemplatedApp | null = null;
#httpServer: HttpServer | null = null;
#listenSocket: us_listen_socket | null = null;
#host: string | null = null;
#connector: ConnectorsByName[Flavor];
#websocketServer: WebsocketServer | null = null;

Expand Down Expand Up @@ -183,8 +192,7 @@ export class Server<
(typeof port !== "number" && typeof port !== "string") ||
(typeof port === "string" && (<string>port).trim().length === 0) ||
+port !== +port >>> 0 ||
port > 0xffff ||
port === 0
port > 0xffff
) {
const err = new Error(
`Port should be >= 0 and < 65536. Received ${port}.`
Expand Down Expand Up @@ -239,6 +247,7 @@ export class Server<
if (listenSocket) {
this.#status = ServerStatus.open;
this.#listenSocket = listenSocket;
this.#host = (host as string) || DEFAULT_HOST;
} else {
this.#status = ServerStatus.closed;
const err = new Error(
Expand Down Expand Up @@ -285,6 +294,19 @@ export class Server<
}
}

public address() {
if (this.#listenSocket) {
const address = this.#host;
return {
address,
family: isIPv4(address) ? "IPv4" : "IPv6",
port: us_socket_local_port(this.#listenSocket)
};
} else {
return null;
}
}

public async close() {
if (this.#status === ServerStatus.opening) {
// if opening
Expand Down
Loading

0 comments on commit a952ae7

Please sign in to comment.