Skip to content

Commit a10c7cf

Browse files
authored
fix: use the host in options to check if port is available (#4385)
1 parent ff1fce7 commit a10c7cf

File tree

5 files changed

+80
-38
lines changed

5 files changed

+80
-38
lines changed

lib/Server.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,10 @@ class Server {
385385

386386
/**
387387
* @param {Port} port
388+
* @param {string} host
388389
* @returns {Promise<number | string>}
389390
*/
390-
static async getFreePort(port) {
391+
static async getFreePort(port, host) {
391392
if (typeof port !== "undefined" && port !== null && port !== "auto") {
392393
return port;
393394
}
@@ -406,7 +407,7 @@ class Server {
406407
? parseInt(process.env.WEBPACK_DEV_SERVER_PORT_RETRY, 10)
407408
: 3;
408409

409-
return pRetry(() => getPort(basePort), {
410+
return pRetry(() => getPort(basePort, host), {
410411
retries: defaultPortRetry,
411412
});
412413
}
@@ -3200,7 +3201,8 @@ class Server {
32003201
/** @type {Host} */ (this.options.host)
32013202
);
32023203
this.options.port = await Server.getFreePort(
3203-
/** @type {Port} */ (this.options.port)
3204+
/** @type {Port} */ (this.options.port),
3205+
this.options.host
32043206
);
32053207
}
32063208

lib/getPort.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,26 @@ const getAvailablePort = async (port, hosts) => {
8888

8989
/**
9090
* @param {number} basePort
91+
* @param {string=} host
9192
* @return {Promise<number>}
9293
*/
93-
async function getPorts(basePort) {
94+
async function getPorts(basePort, host) {
9495
if (basePort < minPort || basePort > maxPort) {
9596
throw new Error(`Port number must lie between ${minPort} and ${maxPort}`);
9697
}
9798

9899
let port = basePort;
99-
const hosts = getLocalHosts();
100+
const localhosts = getLocalHosts();
101+
let hosts;
102+
if (host && !localhosts.has(host)) {
103+
hosts = new Set([host]);
104+
} else {
105+
/* If the host is equivalent to localhost
106+
we need to check every equivalent host
107+
else the port might falsely appear as available
108+
on some operating systems */
109+
hosts = localhosts;
110+
}
100111
/** @type {Set<string | undefined>} */
101112
const portUnavailableErrors = new Set(["EADDRINUSE", "EACCES"]);
102113
while (port <= maxPort) {

test/server/get-port.test.js

+19
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ it("should pick the next port if the preferred port is unavailable", async () =>
1616
server.unref();
1717
await util.promisify(server.listen.bind(server))(preferredPort);
1818
const port = await getPort(preferredPort);
19+
server.close();
1920
expect(port).toBe(preferredPort + 1);
2021
});
2122

@@ -34,3 +35,21 @@ it("should reject too high port numbers", async () => {
3435
expect(e.message).toBeDefined();
3536
}
3637
});
38+
39+
describe("when passing a host", () => {
40+
it("should bind to the preferred port", async () => {
41+
const preferredPort = 8080;
42+
const port = await getPort(8080, "127.0.0.1");
43+
expect(port).toBe(preferredPort);
44+
});
45+
46+
it("should pick the next port if the preferred port is unavailable", async () => {
47+
const preferredPort = 8345;
48+
const server = net.createServer();
49+
server.unref();
50+
await util.promisify(server.listen.bind(server))(preferredPort);
51+
const port = await getPort(preferredPort, "0.0.0.0");
52+
server.close();
53+
expect(port).toBe(preferredPort + 1);
54+
});
55+
});

types/lib/Server.d.ts

+38-32
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ declare class Server {
664664
};
665665
/**
666666
* @param {Port} port
667+
* @param {string} host
667668
* @returns {Promise<number | string>}
668669
*/
669670
https: {
@@ -716,9 +717,9 @@ declare class Server {
716717
description: string;
717718
multiple: boolean;
718719
path: string;
719-
type: string;
720+
type: string /** @type {WebSocketURL} */;
720721
}[];
721-
/** @type {ClientConfiguration} */ description: string;
722+
description: string;
722723
multiple: boolean;
723724
simpleType: string;
724725
};
@@ -730,7 +731,7 @@ declare class Server {
730731
path: string;
731732
}[];
732733
description: string;
733-
simpleType: string;
734+
/** @type {ServerConfiguration} */ simpleType: string;
734735
multiple: boolean;
735736
};
736737
"https-cert-reset": {
@@ -881,7 +882,7 @@ declare class Server {
881882
configs: (
882883
| {
883884
type: string;
884-
/** @type {MultiCompiler} */ multiple: boolean;
885+
multiple: boolean;
885886
description: string;
886887
path: string;
887888
}
@@ -905,6 +906,10 @@ declare class Server {
905906
path: string;
906907
}[];
907908
description: string;
909+
/**
910+
* @private
911+
* @returns {Promise<void>}
912+
*/
908913
simpleType: string;
909914
multiple: boolean;
910915
};
@@ -952,6 +957,10 @@ declare class Server {
952957
simpleType: string;
953958
multiple: boolean;
954959
};
960+
/**
961+
* @param {string | Static | undefined} [optionsForStatic]
962+
* @returns {NormalizedStatic}
963+
*/
955964
"open-target-reset": {
956965
configs: {
957966
type: string;
@@ -1118,7 +1127,7 @@ declare class Server {
11181127
"server-options-pfx-reset": {
11191128
configs: {
11201129
description: string;
1121-
multiple: boolean;
1130+
/** @type {ServerConfiguration} */ multiple: boolean;
11221131
path: string;
11231132
type: string;
11241133
}[];
@@ -1131,7 +1140,7 @@ declare class Server {
11311140
description: string;
11321141
negatedDescription: string;
11331142
multiple: boolean;
1134-
/** @type {ServerOptions} */ path: string;
1143+
path: string;
11351144
type: string;
11361145
}[];
11371146
description: string;
@@ -1144,10 +1153,10 @@ declare class Server {
11441153
multiple: boolean;
11451154
path: string;
11461155
type: string;
1147-
values: string[];
1156+
/** @type {ServerOptions} */ values: string[];
11481157
}[];
11491158
description: string;
1150-
multiple: boolean;
1159+
/** @type {Array<keyof ServerOptions>} */ multiple: boolean;
11511160
simpleType: string;
11521161
};
11531162
static: {
@@ -1167,7 +1176,7 @@ declare class Server {
11671176
}
11681177
)[];
11691178
description: string;
1170-
simpleType: string;
1179+
/** @type {ServerOptions} */ simpleType: string;
11711180
multiple: boolean;
11721181
};
11731182
"static-directory": {
@@ -1279,7 +1288,7 @@ declare class Server {
12791288
}
12801289
| {
12811290
description: string;
1282-
multiple: boolean;
1291+
/** @type {ServerOptions & { cacert?: ServerOptions["ca"] }} */ multiple: boolean;
12831292
path: string;
12841293
type: string;
12851294
}
@@ -1292,6 +1301,7 @@ declare class Server {
12921301
configs: (
12931302
| {
12941303
description: string;
1304+
/** @type {ServerOptions & { cacert?: ServerOptions["ca"] }} */
12951305
multiple: boolean;
12961306
path: string;
12971307
type: string;
@@ -1300,13 +1310,13 @@ declare class Server {
13001310
| {
13011311
description: string;
13021312
multiple: boolean;
1303-
/** @type {ServerOptions} */ path: string;
1304-
type: string;
1313+
path: string;
1314+
/** @type {ServerOptions} */ type: string;
13051315
}
13061316
)[];
13071317
description: string;
13081318
simpleType: string;
1309-
multiple: boolean;
1319+
/** @type {ServerOptions} */ multiple: boolean;
13101320
};
13111321
};
13121322
readonly processArguments: (
@@ -2033,6 +2043,9 @@ declare class Server {
20332043
instanceof?: undefined;
20342044
}
20352045
)[];
2046+
/**
2047+
* @returns {string}
2048+
*/
20362049
};
20372050
instanceof?: undefined;
20382051
}
@@ -2073,6 +2086,9 @@ declare class Server {
20732086
exclude: boolean;
20742087
};
20752088
};
2089+
/**
2090+
* @type {string[]}
2091+
*/
20762092
Headers: {
20772093
anyOf: (
20782094
| {
@@ -2111,7 +2127,7 @@ declare class Server {
21112127
}
21122128
| {
21132129
type: string;
2114-
description: string;
2130+
/** @type {ClientConfiguration} */ description: string;
21152131
link: string;
21162132
cli?: undefined /** @typedef {import("express").Request} Request */;
21172133
}
@@ -2122,7 +2138,6 @@ declare class Server {
21222138
Host: {
21232139
description: string;
21242140
link: string;
2125-
/** @type {ServerConfiguration} */
21262141
anyOf: (
21272142
| {
21282143
enum: string[];
@@ -2152,7 +2167,7 @@ declare class Server {
21522167
}
21532168
)[];
21542169
description: string;
2155-
link: string;
2170+
/** @type {string} */ link: string;
21562171
};
21572172
IPC: {
21582173
anyOf: (
@@ -2261,12 +2276,6 @@ declare class Server {
22612276
minLength: number;
22622277
};
22632278
minItems: number;
2264-
/**
2265-
* prependEntry Method for webpack 4
2266-
* @param {any} originalEntry
2267-
* @param {any} newAdditionalEntries
2268-
* @returns {any}
2269-
*/
22702279
minLength?: undefined;
22712280
}
22722281
| {
@@ -2317,6 +2326,7 @@ declare class Server {
23172326
enum?: undefined;
23182327
}
23192328
| {
2329+
/** @type {any} */
23202330
type: string;
23212331
minLength: number;
23222332
minimum?: undefined;
@@ -2372,7 +2382,7 @@ declare class Server {
23722382
ServerEnum: {
23732383
enum: string[];
23742384
cli: {
2375-
exclude: boolean;
2385+
exclude: boolean /** @type {MultiCompiler} */;
23762386
};
23772387
};
23782388
ServerString: {
@@ -2403,10 +2413,6 @@ declare class Server {
24032413
passphrase: {
24042414
type: string;
24052415
description: string;
2406-
/**
2407-
* @private
2408-
* @returns {Promise<void>}
2409-
*/
24102416
};
24112417
requestCert: {
24122418
type: string;
@@ -2776,7 +2782,7 @@ declare class Server {
27762782
| {
27772783
type: string;
27782784
minLength: number;
2779-
items?: undefined;
2785+
/** @type {ServerConfiguration} */ items?: undefined;
27802786
}
27812787
)[];
27822788
description: string;
@@ -2798,8 +2804,8 @@ declare class Server {
27982804
anyOf: {
27992805
$ref: string;
28002806
}[];
2801-
description: string;
2802-
/** @type {Array<keyof ServerOptions>} */ link: string;
2807+
/** @type {ServerOptions} */ description: string;
2808+
link: string;
28032809
};
28042810
WebSocketServerType: {
28052811
enum: string[];
@@ -2857,7 +2863,6 @@ declare class Server {
28572863
bonjour: {
28582864
$ref: string;
28592865
};
2860-
/** @type {any} */
28612866
client: {
28622867
$ref: string;
28632868
};
@@ -2959,9 +2964,10 @@ declare class Server {
29592964
static getHostname(hostname: Host): Promise<string>;
29602965
/**
29612966
* @param {Port} port
2967+
* @param {string} host
29622968
* @returns {Promise<number | string>}
29632969
*/
2964-
static getFreePort(port: Port): Promise<number | string>;
2970+
static getFreePort(port: Port, host: string): Promise<number | string>;
29652971
/**
29662972
* @returns {string}
29672973
*/

types/lib/getPort.d.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
export = getPorts;
22
/**
33
* @param {number} basePort
4+
* @param {string=} host
45
* @return {Promise<number>}
56
*/
6-
declare function getPorts(basePort: number): Promise<number>;
7+
declare function getPorts(
8+
basePort: number,
9+
host?: string | undefined
10+
): Promise<number>;

0 commit comments

Comments
 (0)