diff --git a/lib/Server.js b/lib/Server.js index b74614ea15..a2066637bb 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -1633,7 +1633,9 @@ class Server { ) { this.sendMessage([client], "error", "Invalid Host/Origin header"); - client.terminate(); + // With https enabled, the sendMessage above is encrypted asynchronously so not yet sent + // Terminate would prevent it sending, so use close to allow it to be sent + client.close(); return; } diff --git a/setupTest.js b/setupTest.js index 0c7c3a98e2..e1624b9bc5 100644 --- a/setupTest.js +++ b/setupTest.js @@ -3,3 +3,10 @@ process.env.CHOKIDAR_USEPOLLING = true; jest.setTimeout(140000); + +global.afterEach(() => { + // We add listeners to signals when creating a new Server instance, but have no cleanup method + // So ensure they are removed after each run to prevent EventEmitter memory leak warnings + process.removeAllListeners("SIGINT"); + process.removeAllListeners("SIGTERM"); +}); diff --git a/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack4 b/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack4 index 98f1f1b688..862641d603 100644 --- a/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack4 +++ b/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack4 @@ -278,6 +278,30 @@ Array [ exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header ("ws"): page errors 1`] = `Array []`; +exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header when "https" is enabled ("sockjs"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Invalid Host/Origin header", + "[webpack-dev-server] Disconnected!", + "[webpack-dev-server] Trying to reconnect...", +] +`; + +exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header when "https" is enabled ("sockjs"): page errors 1`] = `Array []`; + +exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header when "https" is enabled ("ws"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Invalid Host/Origin header", + "[webpack-dev-server] Disconnected!", + "[webpack-dev-server] Trying to reconnect...", +] +`; + +exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header when "https" is enabled ("ws"): page errors 1`] = `Array []`; + exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "origin" header ("sockjs"): console messages 1`] = ` Array [ "[HMR] Waiting for update signal from WDS...", diff --git a/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack5 b/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack5 index 98f1f1b688..862641d603 100644 --- a/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack5 +++ b/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack5 @@ -278,6 +278,30 @@ Array [ exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header ("ws"): page errors 1`] = `Array []`; +exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header when "https" is enabled ("sockjs"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Invalid Host/Origin header", + "[webpack-dev-server] Disconnected!", + "[webpack-dev-server] Trying to reconnect...", +] +`; + +exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header when "https" is enabled ("sockjs"): page errors 1`] = `Array []`; + +exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header when "https" is enabled ("ws"): console messages 1`] = ` +Array [ + "[HMR] Waiting for update signal from WDS...", + "Hey.", + "[webpack-dev-server] Invalid Host/Origin header", + "[webpack-dev-server] Disconnected!", + "[webpack-dev-server] Trying to reconnect...", +] +`; + +exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header when "https" is enabled ("ws"): page errors 1`] = `Array []`; + exports[`allowed hosts should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "origin" header ("sockjs"): console messages 1`] = ` Array [ "[HMR] Waiting for update signal from WDS...", diff --git a/test/e2e/allowed-hosts.test.js b/test/e2e/allowed-hosts.test.js index 4db6a51a20..d014b3e448 100644 --- a/test/e2e/allowed-hosts.test.js +++ b/test/e2e/allowed-hosts.test.js @@ -88,6 +88,85 @@ describe("allowed hosts", () => { await server.stop(); }); + it(`should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "host" header when "https" is enabled ("${webSocketServer}")`, async () => { + const devServerHost = "127.0.0.1"; + const devServerPort = port1; + const proxyHost = devServerHost; + const proxyPort = port2; + + const compiler = webpack(config); + const devServerOptions = { + client: { + webSocketURL: { + port: port2, + protocol: "ws", + }, + }, + webSocketServer, + port: devServerPort, + host: devServerHost, + allowedHosts: "auto", + https: true, + }; + const server = new Server(devServerOptions, compiler); + + await server.start(); + + function startProxy(callback) { + const app = express(); + + app.use( + "/", + createProxyMiddleware({ + // Emulation + onProxyReqWs: (proxyReq) => { + proxyReq.setHeader("host", "my-test-host"); + }, + target: `https://${devServerHost}:${devServerPort}`, + secure: false, + ws: true, + changeOrigin: true, + logLevel: "warn", + }) + ); + + return app.listen(proxyPort, proxyHost, callback); + } + + const proxy = await new Promise((resolve) => { + const proxyCreated = startProxy(() => { + resolve(proxyCreated); + }); + }); + + const { page, browser } = await runBrowser(); + + const pageErrors = []; + const consoleMessages = []; + + page + .on("console", (message) => { + consoleMessages.push(message); + }) + .on("pageerror", (error) => { + pageErrors.push(error); + }); + + await page.goto(`http://${proxyHost}:${proxyPort}/main`, { + waitUntil: "networkidle0", + }); + + expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( + "console messages" + ); + expect(pageErrors).toMatchSnapshot("page errors"); + + proxy.close(); + + await browser.close(); + await server.stop(); + }); + it(`should disconnect web socket client using custom hostname from web socket server with the "auto" value based on the "origin" header ("${webSocketServer}")`, async () => { const devServerHost = "127.0.0.1"; const devServerPort = port1;