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

Fixes #2580 - parse port to number type if provided in a different type #2610

Merged
merged 3 commits into from
Mar 17, 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
25 changes: 21 additions & 4 deletions src/packages/core/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,23 @@ export class Server<
host = null;
}
const callbackIsFunction = typeof callback === "function";

// Method signature specifies port: number, but we parse a string if provided
// inspiration taken from nodejs internal port validator
// https://github.com/nodejs/node/blob/8c4b8b201ada6b76d5306c9c7f352e45087fb4a9/lib/internal/validators.js#L208-L219
if ((typeof port !== 'number' && typeof port !== 'string') ||
(typeof port === 'string' && (<string>port).trim().length === 0) ||
+port !== (+port >>> 0) ||
port > 0xFFFF ||
port === 0) {
const err = new Error(`Port should be >= 0 and < 65536. Received ${port}.`);

return callbackIsFunction
? process.nextTick(callback!, err)
: Promise.reject(err);
}
const portNumber = +port;

const status = this.#status;
if (status === ServerStatus.closing) {
// if closing
Expand All @@ -195,7 +212,7 @@ export class Server<
} else if ((status & ServerStatus.openingOrOpen) !== 0) {
// if opening or open
const err = new Error(
`Server is already open, or is opening, on port: ${port}.`
`Server is already open, or is opening, on port: ${portNumber}.`
);
return callbackIsFunction
? process.nextTick(callback!, err)
Expand All @@ -214,11 +231,11 @@ export class Server<
host
? (this.#app as any).listen(
host,
port,
portNumber,
LIBUS_LISTEN_EXCLUSIVE_PORT,
resolve
)
: this.#app.listen(port, LIBUS_LISTEN_EXCLUSIVE_PORT, resolve);
: this.#app.listen(portNumber, LIBUS_LISTEN_EXCLUSIVE_PORT, resolve);
}
).then(listenSocket => {
if (listenSocket) {
Expand All @@ -228,7 +245,7 @@ export class Server<
this.#status = ServerStatus.closed;
const err = new Error(
`listen EADDRINUSE: address already in use ${host || DEFAULT_HOST
}:${port}.`
}:${portNumber}.`
);
throw err;
}
Expand Down
49 changes: 45 additions & 4 deletions src/packages/core/tests/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ describe("server", () => {
(process.env.GITHUB_ACTION ? describe : describe.skip)("listen", function () {
/**
* Sends a post request to the server and returns the response.
* @param address
* @param port
* @param json
* @returns
* @param address
* @param port
* @param json
* @returns
*/
function post(host: string, port: number, json: any) {
const data = JSON.stringify(json);
Expand Down Expand Up @@ -299,6 +299,47 @@ describe("server", () => {
});
});

it("accepts port as number type or binary, octal, decimal or hexadecimal string", async () => {
const validPorts = [
port, `0b${port.toString(2)}`,
`0o${port.toString(8)}`, port.toString(10),
`0x${port.toString(16)}`
];

for (const specificPort of validPorts) {
s = Ganache.server(defaultOptions);
await s.listen(<any>specificPort);

try {
const req = request.post(`http://localhost:${+specificPort}`);
await req.send(jsonRpcJson);
} finally {
await teardown();
}
}
});

it("fails with invalid ports", async () => {
const invalidPorts = [
-1, 'a', {}, [], false, true,
0xFFFF + 1, Infinity, -Infinity, NaN,
undefined, null, '', ' ', 1.1, '0x',
'-0x1', '-0o1', '-0b1', '0o', '0b', 0
];

for (const specificPort of invalidPorts) {
s = Ganache.server(defaultOptions);

try {
await assert.rejects(s.listen(<any>specificPort), {
message: `Port should be >= 0 and < 65536. Received ${specificPort}.`
});
} finally {
await teardown();
}
}
});

davidmurdoch marked this conversation as resolved.
Show resolved Hide resolved
it("fails to `.listen()` twice, Promise", async () => {
await setup();
try {
Expand Down