From 3d79c70071c2105ac9c8d69d5af3109475a58068 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Fri, 16 Aug 2024 19:33:16 +0300 Subject: [PATCH 1/9] fix: ipv6 output --- lib/Server.js | 32 +++++++++++++++++++++----------- test/cli/host-option.test.js | 4 ++-- test/e2e/host.test.js | 4 ++-- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/lib/Server.js b/lib/Server.js index 82feaa6a41..82640fb8f0 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -388,10 +388,10 @@ class Server { /** * @param {string} gatewayOrFamily or family - * @param {boolean} [isInternal=false] ip should be internal + * @param {boolean} [isInternal] ip should be internal * @returns {string | undefined} */ - static findIp(gatewayOrFamily, isInternal = false) { + static findIp(gatewayOrFamily, isInternal) { if (gatewayOrFamily === "v4" || gatewayOrFamily === "v6") { let host; @@ -406,14 +406,21 @@ class Server { return false; } - if (network.internal !== isInternal) { + if ( + typeof isInternal !== "undefined" && + network.internal !== isInternal + ) { return false; } if (gatewayOrFamily === "v6") { const range = ipaddr.parse(network.address).range(); - if (range !== "ipv4Mapped" && range !== "uniqueLocal") { + if ( + range !== "ipv4Mapped" && + range !== "uniqueLocal" && + range !== "loopback" + ) { return false; } } @@ -458,7 +465,7 @@ class Server { * @returns {Promise} */ static async internalIP(family) { - return Server.findIp(family); + return Server.findIp(family, false); } // TODO remove me in the next major release, we have `findIp` @@ -467,7 +474,7 @@ class Server { * @returns {string | undefined} */ static internalIPSync(family) { - return Server.findIp(family); + return Server.findIp(family, false); } /** @@ -476,11 +483,13 @@ class Server { */ static async getHostname(hostname) { if (hostname === "local-ip") { - return Server.findIp("v4") || Server.findIp("v6") || "0.0.0.0"; + return ( + Server.findIp("v4", false) || Server.findIp("v6", false) || "0.0.0.0" + ); } else if (hostname === "local-ipv4") { - return Server.findIp("v4") || "0.0.0.0"; + return Server.findIp("v4", false) || "0.0.0.0"; } else if (hostname === "local-ipv6") { - return Server.findIp("v6") || "::"; + return Server.findIp("v6", false) || "::"; } return hostname; @@ -2829,14 +2838,15 @@ class Server { if (parsedIP.range() === "unspecified") { localhost = prettyPrintURL("localhost"); + loopbackIPv6 = prettyPrintURL("::1"); - const networkIPv4 = Server.findIp("v4"); + const networkIPv4 = Server.findIp("v4", false); if (networkIPv4) { networkUrlIPv4 = prettyPrintURL(networkIPv4); } - const networkIPv6 = Server.findIp("v6"); + const networkIPv6 = Server.findIp("v6", false); if (networkIPv6) { networkUrlIPv6 = prettyPrintURL(networkIPv6); diff --git a/test/cli/host-option.test.js b/test/cli/host-option.test.js index f2a0066bc0..7bac842e2c 100644 --- a/test/cli/host-option.test.js +++ b/test/cli/host-option.test.js @@ -4,8 +4,8 @@ const { testBin, normalizeStderr } = require("../helpers/test-bin"); const port = require("../ports-map")["cli-host"]; const Server = require("../../lib/Server"); -const localIPv4 = Server.findIp("v4"); -const localIPv6 = Server.findIp("v6"); +const localIPv4 = Server.findIp("v4", false); +const localIPv6 = Server.findIp("v6", false); describe('"host" CLI option', () => { it('should work using "--host 0.0.0.0" (IPv4)', async () => { diff --git a/test/e2e/host.test.js b/test/e2e/host.test.js index a1bb32c091..27a0a408ea 100644 --- a/test/e2e/host.test.js +++ b/test/e2e/host.test.js @@ -6,8 +6,8 @@ const config = require("../fixtures/client-config/webpack.config"); const runBrowser = require("../helpers/run-browser"); const port = require("../ports-map").host; -const ipv4 = Server.findIp("v4"); -const ipv6 = Server.findIp("v6"); +const ipv4 = Server.findIp("v4", false); +const ipv6 = Server.findIp("v6", false); // macos requires root for using ip v6 const isMacOS = process.platform === "darwin"; From 57f842a124f423475b91f098bbe29d2c49cc72d3 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Fri, 16 Aug 2024 19:35:59 +0300 Subject: [PATCH 2/9] fix: types --- types/lib/Server.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/lib/Server.d.ts b/types/lib/Server.d.ts index 05e3aec532..4c882938ad 100644 --- a/types/lib/Server.d.ts +++ b/types/lib/Server.d.ts @@ -1126,7 +1126,7 @@ declare class Server< static isAbsoluteURL(URL: string): boolean; /** * @param {string} gatewayOrFamily or family - * @param {boolean} [isInternal=false] ip should be internal + * @param {boolean} [isInternal] ip should be internal * @returns {string | undefined} */ static findIp( From 3642557207542a7ada83c91553a9fbcc3f548c26 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Fri, 16 Aug 2024 20:30:17 +0300 Subject: [PATCH 3/9] test: refactr --- test/e2e/host.test.js | 110 +++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 67 deletions(-) diff --git a/test/e2e/host.test.js b/test/e2e/host.test.js index 27a0a408ea..64cdd0d54b 100644 --- a/test/e2e/host.test.js +++ b/test/e2e/host.test.js @@ -8,21 +8,23 @@ const port = require("../ports-map").host; const ipv4 = Server.findIp("v4", false); const ipv6 = Server.findIp("v6", false); -// macos requires root for using ip v6 -const isMacOS = process.platform === "darwin"; function getAddress(host, hostname) { let address; if ( typeof host === "undefined" || - (typeof host === "string" && host === "") + (typeof host === "string" && (host === "" || host === "::")) ) { address = "::"; - } else if (typeof host === "string" && host === "0.0.0.0") { + } else if (host === "0.0.0.0") { address = "0.0.0.0"; - } else if (typeof host === "string" && host === "localhost") { - address = parseFloat(process.versions.node) >= 18 ? "::1" : "127.0.0.1"; + } else if (host === "::1") { + address = "::1"; + } else if (host === "localhost") { + address = "127.0.0.1"; + } else if (host === "local-ipv6") { + address = "::"; } else { address = hostname; } @@ -37,28 +39,17 @@ describe("host", () => { undefined, "0.0.0.0", "::", - "localhost", "::1", + "localhost", "127.0.0.1", "local-ip", "local-ipv4", "local-ipv6", ]; - for (let host of hosts) { + for (const host of hosts) { it(`should work using "${host}" host and port as number`, async () => { const compiler = webpack(config); - - if (!ipv6 || isMacOS) { - if (host === "::") { - host = "127.0.0.1"; - } else if (host === "::1") { - host = "127.0.0.1"; - } else if (host === "local-ipv6") { - host = "127.0.0.1"; - } - } - const devServerOptions = { port }; if (host !== "") { @@ -69,19 +60,21 @@ describe("host", () => { let hostname = host; - if (hostname === "0.0.0.0") { - hostname = "127.0.0.1"; - } else if ( - hostname === "" || - typeof hostname === "undefined" || - hostname === "::" || - hostname === "::1" - ) { + if (hostname === "" || typeof hostname === "undefined") { + // 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. + hostname = ipv6 ? `[${ipv6}]` : ipv4; + } else if (hostname === "0.0.0.0") { + hostname = ipv4; + } else if (hostname === "::") { + // 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). + hostname = ipv6 ? `[${ipv6}]` : ipv4; + } else if (hostname === "::1") { hostname = "[::1]"; } else if (hostname === "local-ip" || hostname === "local-ipv4") { hostname = ipv4; } else if (hostname === "local-ipv6") { - hostname = `[${ipv6}]`; + // For test env where network ipv6 doesn't work + hostname = ipv6 ? `[${ipv6}]` : "[::1]"; } await server.start(); @@ -121,17 +114,6 @@ describe("host", () => { it(`should work using "${host}" host and port as string`, async () => { const compiler = webpack(config); - - if (!ipv6 || isMacOS) { - if (host === "::") { - host = "127.0.0.1"; - } else if (host === "::1") { - host = "127.0.0.1"; - } else if (host === "local-ipv6") { - host = "127.0.0.1"; - } - } - const devServerOptions = { port: `${port}` }; if (host !== "") { @@ -142,19 +124,21 @@ describe("host", () => { let hostname = host; - if (hostname === "0.0.0.0") { - hostname = "127.0.0.1"; - } else if ( - hostname === "" || - typeof hostname === "undefined" || - hostname === "::" || - hostname === "::1" - ) { + if (hostname === "" || typeof hostname === "undefined") { + // 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. + hostname = ipv6 ? `[${ipv6}]` : ipv4; + } else if (hostname === "0.0.0.0") { + hostname = ipv4; + } else if (hostname === "::") { + // 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). + hostname = ipv6 ? `[${ipv6}]` : ipv4; + } else if (hostname === "::1") { hostname = "[::1]"; } else if (hostname === "local-ip" || hostname === "local-ipv4") { hostname = ipv4; } else if (hostname === "local-ipv6") { - hostname = `[${ipv6}]`; + // For test env where network ipv6 doesn't work + hostname = ipv6 ? `[${ipv6}]` : "[::1]"; } await server.start(); @@ -197,16 +181,6 @@ describe("host", () => { process.env.WEBPACK_DEV_SERVER_BASE_PORT = port; - if (!ipv6 || isMacOS) { - if (host === "::") { - host = "127.0.0.1"; - } else if (host === "::1") { - host = "127.0.0.1"; - } else if (host === "local-ipv6") { - host = "127.0.0.1"; - } - } - const devServerOptions = { port: "auto" }; if (host !== "") { @@ -217,19 +191,21 @@ describe("host", () => { let hostname = host; - if (hostname === "0.0.0.0") { - hostname = "127.0.0.1"; - } else if ( - hostname === "" || - typeof hostname === "undefined" || - hostname === "::" || - hostname === "::1" - ) { + if (hostname === "" || typeof hostname === "undefined") { + // 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. + hostname = ipv6 ? `[${ipv6}]` : ipv4; + } else if (hostname === "0.0.0.0") { + hostname = ipv4; + } else if (hostname === "::") { + // 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). + hostname = ipv6 ? `[${ipv6}]` : ipv4; + } else if (hostname === "::1") { hostname = "[::1]"; } else if (hostname === "local-ip" || hostname === "local-ipv4") { hostname = ipv4; } else if (hostname === "local-ipv6") { - hostname = `[${ipv6}]`; + // For test env where network ipv6 doesn't work + hostname = ipv6 ? `[${ipv6}]` : "[::1]"; } await server.start(); From d3a4239bc95988e0e30a1cc5f3ddaa5f24864774 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Fri, 16 Aug 2024 20:48:20 +0300 Subject: [PATCH 4/9] test: fix --- test/e2e/host.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/e2e/host.test.js b/test/e2e/host.test.js index 64cdd0d54b..003dd1514f 100644 --- a/test/e2e/host.test.js +++ b/test/e2e/host.test.js @@ -8,6 +8,8 @@ const port = require("../ports-map").host; const ipv4 = Server.findIp("v4", false); const ipv6 = Server.findIp("v6", false); +// macos requires root for using ip v6 +const isMacOS = process.platform === "darwin"; function getAddress(host, hostname) { let address; @@ -22,7 +24,7 @@ function getAddress(host, hostname) { } else if (host === "::1") { address = "::1"; } else if (host === "localhost") { - address = "127.0.0.1"; + address = isMacOS ? "::1" : "127.0.0.1"; } else if (host === "local-ipv6") { address = "::"; } else { From 0c2a4cb61f1fdadf7dffef73d15676104fa34e35 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Mon, 19 Aug 2024 15:00:21 +0300 Subject: [PATCH 5/9] test: debug --- test/e2e/host.test.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/e2e/host.test.js b/test/e2e/host.test.js index 003dd1514f..dffce22a42 100644 --- a/test/e2e/host.test.js +++ b/test/e2e/host.test.js @@ -81,7 +81,11 @@ describe("host", () => { await server.start(); - expect(server.server.address()).toMatchObject(getAddress(host, hostname)); + if (!isMacOS) { + expect(server.server.address()).toMatchObject( + getAddress(host, hostname), + ); + } const { page, browser } = await runBrowser(); From 075edc27f144b9542965617c90533452f7c96f95 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Mon, 19 Aug 2024 15:14:16 +0300 Subject: [PATCH 6/9] test: fix --- test/e2e/host.test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/e2e/host.test.js b/test/e2e/host.test.js index dffce22a42..73f3178521 100644 --- a/test/e2e/host.test.js +++ b/test/e2e/host.test.js @@ -10,6 +10,7 @@ const ipv4 = Server.findIp("v4", false); const ipv6 = Server.findIp("v6", false); // macos requires root for using ip v6 const isMacOS = process.platform === "darwin"; +const isWindows = process.platform === "win32"; function getAddress(host, hostname) { let address; @@ -24,7 +25,10 @@ function getAddress(host, hostname) { } else if (host === "::1") { address = "::1"; } else if (host === "localhost") { - address = isMacOS ? "::1" : "127.0.0.1"; + address = + isMacOS || (parseFloat(process.versions.node) >= 20 && isWindows) + ? "::1" + : "127.0.0.1"; } else if (host === "local-ipv6") { address = "::"; } else { @@ -81,11 +85,7 @@ describe("host", () => { await server.start(); - if (!isMacOS) { - expect(server.server.address()).toMatchObject( - getAddress(host, hostname), - ); - } + expect(server.server.address()).toMatchObject(getAddress(host, hostname)); const { page, browser } = await runBrowser(); From 919be1cbb71d326b9f41dc024ed8c70060550fb3 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Mon, 19 Aug 2024 15:34:45 +0300 Subject: [PATCH 7/9] test: fix --- test/e2e/host.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/host.test.js b/test/e2e/host.test.js index 73f3178521..c37b0d8023 100644 --- a/test/e2e/host.test.js +++ b/test/e2e/host.test.js @@ -26,7 +26,7 @@ function getAddress(host, hostname) { address = "::1"; } else if (host === "localhost") { address = - isMacOS || (parseFloat(process.versions.node) >= 20 && isWindows) + isMacOS || (parseFloat(process.versions.node) >= 18 && isWindows) ? "::1" : "127.0.0.1"; } else if (host === "local-ipv6") { From 906b0a8d549cb2020ba03a22d022221693f552fd Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Mon, 19 Aug 2024 15:35:05 +0300 Subject: [PATCH 8/9] test: fix --- test/e2e/host.test.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/e2e/host.test.js b/test/e2e/host.test.js index c37b0d8023..10d8fc9089 100644 --- a/test/e2e/host.test.js +++ b/test/e2e/host.test.js @@ -25,10 +25,7 @@ function getAddress(host, hostname) { } else if (host === "::1") { address = "::1"; } else if (host === "localhost") { - address = - isMacOS || (parseFloat(process.versions.node) >= 18 && isWindows) - ? "::1" - : "127.0.0.1"; + address = isMacOS || isWindows ? "::1" : "127.0.0.1"; } else if (host === "local-ipv6") { address = "::"; } else { From 787933600e1627f8eaa4059a149b42e69848e84e Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Mon, 19 Aug 2024 16:29:25 +0300 Subject: [PATCH 9/9] test: fix --- test/e2e/host.test.js | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/test/e2e/host.test.js b/test/e2e/host.test.js index 10d8fc9089..4e6c108647 100644 --- a/test/e2e/host.test.js +++ b/test/e2e/host.test.js @@ -1,5 +1,6 @@ "use strict"; +const http = require("http"); const webpack = require("webpack"); const Server = require("../../lib/Server"); const config = require("../fixtures/client-config/webpack.config"); @@ -8,11 +9,8 @@ const port = require("../ports-map").host; const ipv4 = Server.findIp("v4", false); const ipv6 = Server.findIp("v6", false); -// macos requires root for using ip v6 -const isMacOS = process.platform === "darwin"; -const isWindows = process.platform === "win32"; -function getAddress(host, hostname) { +async function getAddress(host, hostname) { let address; if ( @@ -25,7 +23,29 @@ function getAddress(host, hostname) { } else if (host === "::1") { address = "::1"; } else if (host === "localhost") { - address = isMacOS || isWindows ? "::1" : "127.0.0.1"; + // It can be `127.0.0.1` or `::1` on different OS + const server = http.createServer((req, res) => { + res.statusCode = 200; + res.setHeader("Content-Type", "text/plain"); + res.end("Hello World\n"); + }); + + await new Promise((resolve) => { + server.listen({ host: "localhost", port: 23100 }, resolve); + }); + + address = server.address().address; + + await new Promise((resolve, reject) => { + server.close((err) => { + if (err) { + reject(err); + return; + } + + resolve(); + }); + }); } else if (host === "local-ipv6") { address = "::"; } else { @@ -82,7 +102,9 @@ describe("host", () => { await server.start(); - expect(server.server.address()).toMatchObject(getAddress(host, hostname)); + expect(server.server.address()).toMatchObject( + await getAddress(host, hostname), + ); const { page, browser } = await runBrowser(); @@ -146,7 +168,9 @@ describe("host", () => { await server.start(); - expect(server.server.address()).toMatchObject(getAddress(host, hostname)); + expect(server.server.address()).toMatchObject( + await getAddress(host, hostname), + ); const { page, browser } = await runBrowser(); @@ -213,7 +237,9 @@ describe("host", () => { await server.start(); - expect(server.server.address()).toMatchObject(getAddress(host, hostname)); + expect(server.server.address()).toMatchObject( + await getAddress(host, hostname), + ); const address = server.server.address(); const { page, browser } = await runBrowser();