Skip to content

Commit

Permalink
fix: ipv6 output (#5270)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait authored Aug 19, 2024
1 parent 748d420 commit 06005e7
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 87 deletions.
32 changes: 21 additions & 11 deletions lib/Server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
}
}
Expand Down Expand Up @@ -458,7 +465,7 @@ class Server {
* @returns {Promise<string | undefined>}
*/
static async internalIP(family) {
return Server.findIp(family);
return Server.findIp(family, false);
}

// TODO remove me in the next major release, we have `findIp`
Expand All @@ -467,7 +474,7 @@ class Server {
* @returns {string | undefined}
*/
static internalIPSync(family) {
return Server.findIp(family);
return Server.findIp(family, false);
}

/**
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions test/cli/host-option.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
151 changes: 78 additions & 73 deletions test/e2e/host.test.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,53 @@
"use strict";

const http = require("http");
const webpack = require("webpack");
const Server = require("../../lib/Server");
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");
// macos requires root for using ip v6
const isMacOS = process.platform === "darwin";
const ipv4 = Server.findIp("v4", false);
const ipv6 = Server.findIp("v6", false);

function getAddress(host, hostname) {
async function getAddress(host, hostname) {
let address;

if (
typeof host === "undefined" ||
(typeof host === "string" && host === "<not-specified>")
(typeof host === "string" && (host === "<not-specified>" || 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") {
// 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 {
address = hostname;
}
Expand All @@ -37,28 +62,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 !== "<not-specified>") {
Expand All @@ -69,24 +83,28 @@ describe("host", () => {

let hostname = host;

if (hostname === "0.0.0.0") {
hostname = "127.0.0.1";
} else if (
hostname === "<not-specified>" ||
typeof hostname === "undefined" ||
hostname === "::" ||
hostname === "::1"
) {
if (hostname === "<not-specified>" || 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();

expect(server.server.address()).toMatchObject(getAddress(host, hostname));
expect(server.server.address()).toMatchObject(
await getAddress(host, hostname),
);

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

Expand Down Expand Up @@ -121,17 +139,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 !== "<not-specified>") {
Expand All @@ -142,24 +149,28 @@ describe("host", () => {

let hostname = host;

if (hostname === "0.0.0.0") {
hostname = "127.0.0.1";
} else if (
hostname === "<not-specified>" ||
typeof hostname === "undefined" ||
hostname === "::" ||
hostname === "::1"
) {
if (hostname === "<not-specified>" || 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();

expect(server.server.address()).toMatchObject(getAddress(host, hostname));
expect(server.server.address()).toMatchObject(
await getAddress(host, hostname),
);

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

Expand Down Expand Up @@ -197,16 +208,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 !== "<not-specified>") {
Expand All @@ -217,24 +218,28 @@ describe("host", () => {

let hostname = host;

if (hostname === "0.0.0.0") {
hostname = "127.0.0.1";
} else if (
hostname === "<not-specified>" ||
typeof hostname === "undefined" ||
hostname === "::" ||
hostname === "::1"
) {
if (hostname === "<not-specified>" || 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();

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();
Expand Down
2 changes: 1 addition & 1 deletion types/lib/Server.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 06005e7

Please sign in to comment.