diff --git a/lib/Server.js b/lib/Server.js index 34320d23c2..59141a4319 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -1775,15 +1775,25 @@ class Server { } async stop() { - if (this.webSocketServer) { - process.nextTick(() => { - this.webSocketServer.implementation.close(); - }); + if (this.webSocketProxies.length > 0) { + this.webSocketProxies = []; } - await Promise.all(this.staticWatchers.map((watcher) => watcher.close())); + if (this.staticWatchers.length > 0) { + await Promise.all(this.staticWatchers.map((watcher) => watcher.close())); - this.staticWatchers = []; + this.staticWatchers = []; + } + + if (this.webSocketServer) { + await new Promise((resolve) => { + this.webSocketServer.implementation.close(() => { + resolve(); + }); + }); + + this.webSocketServer = null; + } if (this.server) { await new Promise((resolve) => { @@ -1792,17 +1802,23 @@ class Server { }); }); - await new Promise((resolve, reject) => { - this.middleware.close((error) => { - if (error) { - reject(error); + this.server = null; - return; - } + if (this.middleware) { + await new Promise((resolve, reject) => { + this.middleware.close((error) => { + if (error) { + reject(error); - resolve(); + return; + } + + resolve(); + }); }); - }); + + this.middleware = null; + } } } diff --git a/lib/servers/SockJSServer.js b/lib/servers/SockJSServer.js index db4a7a0de9..66c30daf83 100644 --- a/lib/servers/SockJSServer.js +++ b/lib/servers/SockJSServer.js @@ -74,12 +74,14 @@ module.exports = class SockJSServer extends BaseServer { }); }); - this.implementation.close = () => { + this.implementation.close = (callback) => { for (const client of this.clients) { client.close(); } this.clients.clear(); + + callback(); }; } }; diff --git a/lib/servers/WebsocketServer.js b/lib/servers/WebsocketServer.js index 078238c931..d2639ff9f7 100644 --- a/lib/servers/WebsocketServer.js +++ b/lib/servers/WebsocketServer.js @@ -70,6 +70,10 @@ module.exports = class WebsocketServer extends BaseServer { this.implementation.on("close", () => { clearInterval(interval); + for (const ws of this.clients) { + ws.terminate(); + } + this.clients.clear(); }); } diff --git a/package-lock.json b/package-lock.json index ee28bef714..c330dc8061 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "strip-ansi": "^7.0.0", "url": "^0.11.0", "webpack-dev-middleware": "^5.0.0", - "ws": "^7.5.3" + "ws": "^8.1.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" @@ -83,7 +83,7 @@ "tcp-port-used": "^1.0.2", "typescript": "^4.2.4", "url-loader": "^4.1.1", - "webpack": "^5.48.0", + "webpack": "^5.50.0", "webpack-cli": "^4.7.2", "webpack-merge": "^5.8.0" }, @@ -11649,6 +11649,27 @@ } } }, + "node_modules/jsdom/node_modules/ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -17785,11 +17806,11 @@ } }, "node_modules/ws": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", - "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.1.0.tgz", + "integrity": "sha512-0UWlCD2s3RSclw8FN+D0zDTUyMO+1kHwJQQJzkgUh16S8d3NYON0AKCEQPffE0ez4JyRFu76QDA9KR5bOG/7jw==", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", @@ -26598,6 +26619,15 @@ "whatwg-url": "^8.5.0", "ws": "^7.4.6", "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "dev": true, + "requires": {} + } } }, "jsesc": { @@ -31326,9 +31356,9 @@ } }, "ws": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", - "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.1.0.tgz", + "integrity": "sha512-0UWlCD2s3RSclw8FN+D0zDTUyMO+1kHwJQQJzkgUh16S8d3NYON0AKCEQPffE0ez4JyRFu76QDA9KR5bOG/7jw==", "requires": {} }, "xml-name-validator": { diff --git a/package.json b/package.json index 5cb392af1f..a1ff24714c 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "strip-ansi": "^7.0.0", "url": "^0.11.0", "webpack-dev-middleware": "^5.0.0", - "ws": "^7.5.3" + "ws": "^8.1.0" }, "devDependencies": { "@babel/cli": "^7.14.5", @@ -104,7 +104,7 @@ "tcp-port-used": "^1.0.2", "typescript": "^4.2.4", "url-loader": "^4.1.1", - "webpack": "^5.48.0", + "webpack": "^5.50.0", "webpack-cli": "^4.7.2", "webpack-merge": "^5.8.0" }, diff --git a/test/e2e/web-socket-communication.test.js b/test/e2e/web-socket-communication.test.js index 5f841427f6..7c1cca180e 100644 --- a/test/e2e/web-socket-communication.test.js +++ b/test/e2e/web-socket-communication.test.js @@ -33,7 +33,7 @@ describe("web socket communication", () => { page .on("console", (message) => { - consoleMessages.push(message); + consoleMessages.push(message.text()); }) .on("pageerror", (error) => { pageErrors.push(error); @@ -45,20 +45,18 @@ describe("web socket communication", () => { await server.stop(); await new Promise((resolve) => { const interval = setInterval(() => { - if (server.webSocketServer.clients.size === 0) { + if (consoleMessages.includes("[webpack-dev-server] Disconnected!")) { clearInterval(interval); + resolve(); } }, 100); }); - expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( - "console messages" - ); + expect(consoleMessages).toMatchSnapshot("console messages"); expect(pageErrors).toMatchSnapshot("page errors"); await browser.close(); - await server.stop(); }); it(`should work and terminate client that is not alive ("${websocketServer}")`, async () => { diff --git a/test/server/Server.test.js b/test/server/Server.test.js index a6302028e0..cc27facceb 100644 --- a/test/server/Server.test.js +++ b/test/server/Server.test.js @@ -176,7 +176,11 @@ describe("Server", () => { options: { webSocketServer: class CustomServerImplementation { constructor() { - this.implementation = { close: () => {} }; + this.implementation = { + close: (callback) => { + callback(); + }, + }; } }, }, @@ -192,7 +196,8 @@ describe("Server", () => { type: "ws", options: { host: "127.0.0.1", - port: 43334, + // TODO `jest` is freeze here + // port: 43334, pathname: "/ws", }, }, @@ -210,7 +215,8 @@ describe("Server", () => { type: "ws", options: { host: "127.0.0.1", - port: "43335", + // TODO `jest` is freeze here + // port: "43335", pathname: "/ws", }, }, diff --git a/test/server/__snapshots__/Server.test.js.snap.webpack4 b/test/server/__snapshots__/Server.test.js.snap.webpack4 index f5c0375fe6..6956377a62 100644 --- a/test/server/__snapshots__/Server.test.js.snap.webpack4 +++ b/test/server/__snapshots__/Server.test.js.snap.webpack4 @@ -478,7 +478,6 @@ Object { "host": "127.0.0.1", "path": "/ws", "pathname": "/ws", - "port": 43334, }, "type": "ws", }, @@ -523,7 +522,6 @@ Object { "host": "127.0.0.1", "path": "/ws", "pathname": "/ws", - "port": 43335, }, "type": "ws", }, diff --git a/test/server/__snapshots__/Server.test.js.snap.webpack5 b/test/server/__snapshots__/Server.test.js.snap.webpack5 index f5c0375fe6..6956377a62 100644 --- a/test/server/__snapshots__/Server.test.js.snap.webpack5 +++ b/test/server/__snapshots__/Server.test.js.snap.webpack5 @@ -478,7 +478,6 @@ Object { "host": "127.0.0.1", "path": "/ws", "pathname": "/ws", - "port": 43334, }, "type": "ws", }, @@ -523,7 +522,6 @@ Object { "host": "127.0.0.1", "path": "/ws", "pathname": "/ws", - "port": 43335, }, "type": "ws", }, diff --git a/test/server/http2-option.test.js b/test/server/http2-option.test.js index 90f994bede..6c38805333 100644 --- a/test/server/http2-option.test.js +++ b/test/server/http2-option.test.js @@ -62,6 +62,7 @@ describe('"http2" option', () => { }); http2Req.on("end", () => { expect(data).toEqual(expect.stringMatching(/Heyo/)); + client.close(); done(); }); http2Req.end(); diff --git a/test/server/proxy-option.test.js b/test/server/proxy-option.test.js index cc7f2e872a..e02d5949c8 100644 --- a/test/server/proxy-option.test.js +++ b/test/server/proxy-option.test.js @@ -584,7 +584,7 @@ describe("proxy option", () => { ws = new WebSocket(`ws://localhost:${port3}/proxy3/socket`); ws.on("message", (message) => { - responseMessage = message; + responseMessage = message.toString(); done(); }); @@ -600,6 +600,10 @@ describe("proxy option", () => { afterAll(async () => { webSocketServer.close(); + for (const client of webSocketServer.clients) { + client.terminate(); + } + await server.stop(); }); }); diff --git a/test/server/setupExitSignals-option.test.js b/test/server/setupExitSignals-option.test.js index 6eee7bd1db..6fda5ef375 100644 --- a/test/server/setupExitSignals-option.test.js +++ b/test/server/setupExitSignals-option.test.js @@ -46,10 +46,12 @@ describe("setupExitSignals option", () => { it.each(signals)("should close and exit on %s", (signal, done) => { process.emit(signal); - process.nextTick(() => { + + setTimeout(() => { expect(killSpy.mock.calls.length).toEqual(1); + expect(exitSpy.mock.calls.length).toEqual(1); done(); - }); + }, 1000); }); });