Skip to content

Commit 06005e7

Browse files
authoredAug 19, 2024
fix: ipv6 output (#5270)
1 parent 748d420 commit 06005e7

File tree

4 files changed

+102
-87
lines changed

4 files changed

+102
-87
lines changed
 

‎lib/Server.js

+21-11
Original file line numberDiff line numberDiff line change
@@ -388,10 +388,10 @@ class Server {
388388

389389
/**
390390
* @param {string} gatewayOrFamily or family
391-
* @param {boolean} [isInternal=false] ip should be internal
391+
* @param {boolean} [isInternal] ip should be internal
392392
* @returns {string | undefined}
393393
*/
394-
static findIp(gatewayOrFamily, isInternal = false) {
394+
static findIp(gatewayOrFamily, isInternal) {
395395
if (gatewayOrFamily === "v4" || gatewayOrFamily === "v6") {
396396
let host;
397397

@@ -406,14 +406,21 @@ class Server {
406406
return false;
407407
}
408408

409-
if (network.internal !== isInternal) {
409+
if (
410+
typeof isInternal !== "undefined" &&
411+
network.internal !== isInternal
412+
) {
410413
return false;
411414
}
412415

413416
if (gatewayOrFamily === "v6") {
414417
const range = ipaddr.parse(network.address).range();
415418

416-
if (range !== "ipv4Mapped" && range !== "uniqueLocal") {
419+
if (
420+
range !== "ipv4Mapped" &&
421+
range !== "uniqueLocal" &&
422+
range !== "loopback"
423+
) {
417424
return false;
418425
}
419426
}
@@ -458,7 +465,7 @@ class Server {
458465
* @returns {Promise<string | undefined>}
459466
*/
460467
static async internalIP(family) {
461-
return Server.findIp(family);
468+
return Server.findIp(family, false);
462469
}
463470

464471
// TODO remove me in the next major release, we have `findIp`
@@ -467,7 +474,7 @@ class Server {
467474
* @returns {string | undefined}
468475
*/
469476
static internalIPSync(family) {
470-
return Server.findIp(family);
477+
return Server.findIp(family, false);
471478
}
472479

473480
/**
@@ -476,11 +483,13 @@ class Server {
476483
*/
477484
static async getHostname(hostname) {
478485
if (hostname === "local-ip") {
479-
return Server.findIp("v4") || Server.findIp("v6") || "0.0.0.0";
486+
return (
487+
Server.findIp("v4", false) || Server.findIp("v6", false) || "0.0.0.0"
488+
);
480489
} else if (hostname === "local-ipv4") {
481-
return Server.findIp("v4") || "0.0.0.0";
490+
return Server.findIp("v4", false) || "0.0.0.0";
482491
} else if (hostname === "local-ipv6") {
483-
return Server.findIp("v6") || "::";
492+
return Server.findIp("v6", false) || "::";
484493
}
485494

486495
return hostname;
@@ -2829,14 +2838,15 @@ class Server {
28292838

28302839
if (parsedIP.range() === "unspecified") {
28312840
localhost = prettyPrintURL("localhost");
2841+
loopbackIPv6 = prettyPrintURL("::1");
28322842

2833-
const networkIPv4 = Server.findIp("v4");
2843+
const networkIPv4 = Server.findIp("v4", false);
28342844

28352845
if (networkIPv4) {
28362846
networkUrlIPv4 = prettyPrintURL(networkIPv4);
28372847
}
28382848

2839-
const networkIPv6 = Server.findIp("v6");
2849+
const networkIPv6 = Server.findIp("v6", false);
28402850

28412851
if (networkIPv6) {
28422852
networkUrlIPv6 = prettyPrintURL(networkIPv6);

‎test/cli/host-option.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ const { testBin, normalizeStderr } = require("../helpers/test-bin");
44
const port = require("../ports-map")["cli-host"];
55
const Server = require("../../lib/Server");
66

7-
const localIPv4 = Server.findIp("v4");
8-
const localIPv6 = Server.findIp("v6");
7+
const localIPv4 = Server.findIp("v4", false);
8+
const localIPv6 = Server.findIp("v6", false);
99

1010
describe('"host" CLI option', () => {
1111
it('should work using "--host 0.0.0.0" (IPv4)', async () => {

‎test/e2e/host.test.js

+78-73
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,53 @@
11
"use strict";
22

3+
const http = require("http");
34
const webpack = require("webpack");
45
const Server = require("../../lib/Server");
56
const config = require("../fixtures/client-config/webpack.config");
67
const runBrowser = require("../helpers/run-browser");
78
const port = require("../ports-map").host;
89

9-
const ipv4 = Server.findIp("v4");
10-
const ipv6 = Server.findIp("v6");
11-
// macos requires root for using ip v6
12-
const isMacOS = process.platform === "darwin";
10+
const ipv4 = Server.findIp("v4", false);
11+
const ipv6 = Server.findIp("v6", false);
1312

14-
function getAddress(host, hostname) {
13+
async function getAddress(host, hostname) {
1514
let address;
1615

1716
if (
1817
typeof host === "undefined" ||
19-
(typeof host === "string" && host === "<not-specified>")
18+
(typeof host === "string" && (host === "<not-specified>" || host === "::"))
2019
) {
2120
address = "::";
22-
} else if (typeof host === "string" && host === "0.0.0.0") {
21+
} else if (host === "0.0.0.0") {
2322
address = "0.0.0.0";
24-
} else if (typeof host === "string" && host === "localhost") {
25-
address = parseFloat(process.versions.node) >= 18 ? "::1" : "127.0.0.1";
23+
} else if (host === "::1") {
24+
address = "::1";
25+
} else if (host === "localhost") {
26+
// It can be `127.0.0.1` or `::1` on different OS
27+
const server = http.createServer((req, res) => {
28+
res.statusCode = 200;
29+
res.setHeader("Content-Type", "text/plain");
30+
res.end("Hello World\n");
31+
});
32+
33+
await new Promise((resolve) => {
34+
server.listen({ host: "localhost", port: 23100 }, resolve);
35+
});
36+
37+
address = server.address().address;
38+
39+
await new Promise((resolve, reject) => {
40+
server.close((err) => {
41+
if (err) {
42+
reject(err);
43+
return;
44+
}
45+
46+
resolve();
47+
});
48+
});
49+
} else if (host === "local-ipv6") {
50+
address = "::";
2651
} else {
2752
address = hostname;
2853
}
@@ -37,28 +62,17 @@ describe("host", () => {
3762
undefined,
3863
"0.0.0.0",
3964
"::",
40-
"localhost",
4165
"::1",
66+
"localhost",
4267
"127.0.0.1",
4368
"local-ip",
4469
"local-ipv4",
4570
"local-ipv6",
4671
];
4772

48-
for (let host of hosts) {
73+
for (const host of hosts) {
4974
it(`should work using "${host}" host and port as number`, async () => {
5075
const compiler = webpack(config);
51-
52-
if (!ipv6 || isMacOS) {
53-
if (host === "::") {
54-
host = "127.0.0.1";
55-
} else if (host === "::1") {
56-
host = "127.0.0.1";
57-
} else if (host === "local-ipv6") {
58-
host = "127.0.0.1";
59-
}
60-
}
61-
6276
const devServerOptions = { port };
6377

6478
if (host !== "<not-specified>") {
@@ -69,24 +83,28 @@ describe("host", () => {
6983

7084
let hostname = host;
7185

72-
if (hostname === "0.0.0.0") {
73-
hostname = "127.0.0.1";
74-
} else if (
75-
hostname === "<not-specified>" ||
76-
typeof hostname === "undefined" ||
77-
hostname === "::" ||
78-
hostname === "::1"
79-
) {
86+
if (hostname === "<not-specified>" || typeof hostname === "undefined") {
87+
// If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise.
88+
hostname = ipv6 ? `[${ipv6}]` : ipv4;
89+
} else if (hostname === "0.0.0.0") {
90+
hostname = ipv4;
91+
} else if (hostname === "::") {
92+
// In most operating systems, listening to the unspecified IPv6 address (::) may cause the net.Server to also listen on the unspecified IPv4 address (0.0.0.0).
93+
hostname = ipv6 ? `[${ipv6}]` : ipv4;
94+
} else if (hostname === "::1") {
8095
hostname = "[::1]";
8196
} else if (hostname === "local-ip" || hostname === "local-ipv4") {
8297
hostname = ipv4;
8398
} else if (hostname === "local-ipv6") {
84-
hostname = `[${ipv6}]`;
99+
// For test env where network ipv6 doesn't work
100+
hostname = ipv6 ? `[${ipv6}]` : "[::1]";
85101
}
86102

87103
await server.start();
88104

89-
expect(server.server.address()).toMatchObject(getAddress(host, hostname));
105+
expect(server.server.address()).toMatchObject(
106+
await getAddress(host, hostname),
107+
);
90108

91109
const { page, browser } = await runBrowser();
92110

@@ -121,17 +139,6 @@ describe("host", () => {
121139

122140
it(`should work using "${host}" host and port as string`, async () => {
123141
const compiler = webpack(config);
124-
125-
if (!ipv6 || isMacOS) {
126-
if (host === "::") {
127-
host = "127.0.0.1";
128-
} else if (host === "::1") {
129-
host = "127.0.0.1";
130-
} else if (host === "local-ipv6") {
131-
host = "127.0.0.1";
132-
}
133-
}
134-
135142
const devServerOptions = { port: `${port}` };
136143

137144
if (host !== "<not-specified>") {
@@ -142,24 +149,28 @@ describe("host", () => {
142149

143150
let hostname = host;
144151

145-
if (hostname === "0.0.0.0") {
146-
hostname = "127.0.0.1";
147-
} else if (
148-
hostname === "<not-specified>" ||
149-
typeof hostname === "undefined" ||
150-
hostname === "::" ||
151-
hostname === "::1"
152-
) {
152+
if (hostname === "<not-specified>" || typeof hostname === "undefined") {
153+
// If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise.
154+
hostname = ipv6 ? `[${ipv6}]` : ipv4;
155+
} else if (hostname === "0.0.0.0") {
156+
hostname = ipv4;
157+
} else if (hostname === "::") {
158+
// In most operating systems, listening to the unspecified IPv6 address (::) may cause the net.Server to also listen on the unspecified IPv4 address (0.0.0.0).
159+
hostname = ipv6 ? `[${ipv6}]` : ipv4;
160+
} else if (hostname === "::1") {
153161
hostname = "[::1]";
154162
} else if (hostname === "local-ip" || hostname === "local-ipv4") {
155163
hostname = ipv4;
156164
} else if (hostname === "local-ipv6") {
157-
hostname = `[${ipv6}]`;
165+
// For test env where network ipv6 doesn't work
166+
hostname = ipv6 ? `[${ipv6}]` : "[::1]";
158167
}
159168

160169
await server.start();
161170

162-
expect(server.server.address()).toMatchObject(getAddress(host, hostname));
171+
expect(server.server.address()).toMatchObject(
172+
await getAddress(host, hostname),
173+
);
163174

164175
const { page, browser } = await runBrowser();
165176

@@ -197,16 +208,6 @@ describe("host", () => {
197208

198209
process.env.WEBPACK_DEV_SERVER_BASE_PORT = port;
199210

200-
if (!ipv6 || isMacOS) {
201-
if (host === "::") {
202-
host = "127.0.0.1";
203-
} else if (host === "::1") {
204-
host = "127.0.0.1";
205-
} else if (host === "local-ipv6") {
206-
host = "127.0.0.1";
207-
}
208-
}
209-
210211
const devServerOptions = { port: "auto" };
211212

212213
if (host !== "<not-specified>") {
@@ -217,24 +218,28 @@ describe("host", () => {
217218

218219
let hostname = host;
219220

220-
if (hostname === "0.0.0.0") {
221-
hostname = "127.0.0.1";
222-
} else if (
223-
hostname === "<not-specified>" ||
224-
typeof hostname === "undefined" ||
225-
hostname === "::" ||
226-
hostname === "::1"
227-
) {
221+
if (hostname === "<not-specified>" || typeof hostname === "undefined") {
222+
// If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise.
223+
hostname = ipv6 ? `[${ipv6}]` : ipv4;
224+
} else if (hostname === "0.0.0.0") {
225+
hostname = ipv4;
226+
} else if (hostname === "::") {
227+
// In most operating systems, listening to the unspecified IPv6 address (::) may cause the net.Server to also listen on the unspecified IPv4 address (0.0.0.0).
228+
hostname = ipv6 ? `[${ipv6}]` : ipv4;
229+
} else if (hostname === "::1") {
228230
hostname = "[::1]";
229231
} else if (hostname === "local-ip" || hostname === "local-ipv4") {
230232
hostname = ipv4;
231233
} else if (hostname === "local-ipv6") {
232-
hostname = `[${ipv6}]`;
234+
// For test env where network ipv6 doesn't work
235+
hostname = ipv6 ? `[${ipv6}]` : "[::1]";
233236
}
234237

235238
await server.start();
236239

237-
expect(server.server.address()).toMatchObject(getAddress(host, hostname));
240+
expect(server.server.address()).toMatchObject(
241+
await getAddress(host, hostname),
242+
);
238243

239244
const address = server.server.address();
240245
const { page, browser } = await runBrowser();

‎types/lib/Server.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1126,7 +1126,7 @@ declare class Server<
11261126
static isAbsoluteURL(URL: string): boolean;
11271127
/**
11281128
* @param {string} gatewayOrFamily or family
1129-
* @param {boolean} [isInternal=false] ip should be internal
1129+
* @param {boolean} [isInternal] ip should be internal
11301130
* @returns {string | undefined}
11311131
*/
11321132
static findIp(

0 commit comments

Comments
 (0)