Skip to content

Commit a6501e6

Browse files
refactor!: move web socket client to web socket server class
1 parent 384549f commit a6501e6

8 files changed

+94
-378
lines changed

lib/Server.js

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ class Server {
2929
this.compiler = compiler;
3030
this.options = options;
3131
this.logger = this.compiler.getInfrastructureLogger('webpack-dev-server');
32-
this.webSocketConnections = [];
3332
this.staticWatchers = [];
3433
// Keep track of websocket proxies for external websocket upgrade.
3534
this.webSocketProxies = [];
@@ -193,11 +192,13 @@ class Server {
193192
msg = `${msg} (${addInfo})`;
194193
}
195194

196-
this.sendMessage(this.webSocketConnections, 'progress-update', {
197-
percent,
198-
msg,
199-
pluginName,
200-
});
195+
if (this.webSocketServer) {
196+
this.sendMessage(this.webSocketServer.clients, 'progress-update', {
197+
percent,
198+
msg,
199+
pluginName,
200+
});
201+
}
201202

202203
if (this.server) {
203204
this.server.emit('progress-update', { percent, msg, pluginName });
@@ -212,15 +213,17 @@ class Server {
212213
}
213214

214215
setupHooks() {
215-
// Listening for events
216-
const invalidPlugin = () => {
217-
this.sendMessage(this.webSocketConnections, 'invalid');
218-
};
219-
220216
const addHooks = (compiler) => {
221-
compiler.hooks.invalid.tap('webpack-dev-server', invalidPlugin);
217+
compiler.hooks.invalid.tap('webpack-dev-server', () => {
218+
if (this.webSocketServer) {
219+
this.sendMessage(this.webSocketServer.clients, 'invalid');
220+
}
221+
});
222222
compiler.hooks.done.tap('webpack-dev-server', (stats) => {
223-
this.sendStats(this.webSocketConnections, this.getStats(stats));
223+
if (this.webSocketServer) {
224+
this.sendStats(this.webSocketServer.clients, this.getStats(stats));
225+
}
226+
224227
this.stats = stats;
225228
});
226229
};
@@ -673,15 +676,19 @@ class Server {
673676
// eslint-disable-next-line new-cap
674677
this.webSocketServer = new this.webSocketServerImplementation(this);
675678

676-
this.webSocketServer.onConnection((connection, headers) => {
677-
if (!connection) {
678-
return;
679-
}
679+
this.webSocketServer.implementation.on('connection', (client, request) => {
680+
const headers =
681+
// eslint-disable-next-line no-nested-ternary
682+
typeof request !== 'undefined'
683+
? request.headers
684+
: typeof client.headers !== 'undefined'
685+
? client.headers
686+
: // eslint-disable-next-line no-undefined
687+
undefined;
680688

681689
if (!headers) {
682690
this.logger.warn(
683-
'webSocketServer implementation must pass headers to the callback of onConnection(f) ' +
684-
'via f(connection, headers) in order for clients to pass a headers security check'
691+
'webSocketServer implementation must pass headers for the "connection" event'
685692
);
686693
}
687694

@@ -690,48 +697,34 @@ class Server {
690697
!this.checkHostHeader(headers) ||
691698
!this.checkOriginHeader(headers)
692699
) {
693-
this.sendMessage([connection], 'error', 'Invalid Host/Origin header');
700+
this.sendMessage([client], 'error', 'Invalid Host/Origin header');
694701

695-
this.webSocketServer.closeConnection(connection);
702+
client.terminate();
696703

697704
return;
698705
}
699706

700-
this.webSocketConnections.push(connection);
701-
702-
this.webSocketServer.onConnectionClose(connection, () => {
703-
const idx = this.webSocketConnections.indexOf(connection);
704-
705-
if (idx >= 0) {
706-
this.webSocketConnections.splice(idx, 1);
707-
}
708-
});
709-
710707
if (this.options.hot === true || this.options.hot === 'only') {
711-
this.sendMessage([connection], 'hot');
708+
this.sendMessage([client], 'hot');
712709
}
713710

714711
if (this.options.liveReload) {
715-
this.sendMessage([connection], 'liveReload');
712+
this.sendMessage([client], 'liveReload');
716713
}
717714

718715
if (this.options.client && this.options.client.progress) {
719-
this.sendMessage(
720-
[connection],
721-
'progress',
722-
this.options.client.progress
723-
);
716+
this.sendMessage([client], 'progress', this.options.client.progress);
724717
}
725718

726719
if (this.options.client && this.options.client.overlay) {
727-
this.sendMessage([connection], 'overlay', this.options.client.overlay);
720+
this.sendMessage([client], 'overlay', this.options.client.overlay);
728721
}
729722

730723
if (!this.stats) {
731724
return;
732725
}
733726

734-
this.sendStats([connection], this.getStats(this.stats), true);
727+
this.sendStats([client], this.getStats(this.stats), true);
735728
});
736729
}
737730

@@ -1072,8 +1065,7 @@ class Server {
10721065

10731066
close(callback) {
10741067
if (this.webSocketServer) {
1075-
this.webSocketServer.close();
1076-
this.webSocketConnections = [];
1068+
this.webSocketServer.implementation.close();
10771069
}
10781070

10791071
const prom = Promise.all(
@@ -1235,12 +1227,14 @@ class Server {
12351227
return false;
12361228
}
12371229

1238-
sendMessage(webSocketConnections, type, data) {
1239-
webSocketConnections.forEach((webSocketConnection) => {
1240-
this.webSocketServer.send(
1241-
webSocketConnection,
1242-
JSON.stringify({ type, data })
1243-
);
1230+
// eslint-disable-next-line class-methods-use-this
1231+
sendMessage(clients, type, data) {
1232+
clients.forEach((client) => {
1233+
// `sockjs` uses `1` to indicate client is ready to accept data
1234+
// `ws` uses `WebSocket.OPEN`, but it is mean `1` too
1235+
if (client.readyState === 1) {
1236+
client.send(JSON.stringify({ type, data }));
1237+
}
12441238
});
12451239
}
12461240

@@ -1270,7 +1264,7 @@ class Server {
12701264
}
12711265

12721266
// Send stats to a socket or multiple sockets
1273-
sendStats(webSocketConnections, stats, force) {
1267+
sendStats(clients, stats, force) {
12741268
const shouldEmit =
12751269
!force &&
12761270
stats &&
@@ -1280,23 +1274,23 @@ class Server {
12801274
stats.assets.every((asset) => !asset.emitted);
12811275

12821276
if (shouldEmit) {
1283-
this.sendMessage(webSocketConnections, 'still-ok');
1277+
this.sendMessage(clients, 'still-ok');
12841278

12851279
return;
12861280
}
12871281

1288-
this.sendMessage(webSocketConnections, 'hash', stats.hash);
1282+
this.sendMessage(clients, 'hash', stats.hash);
12891283

12901284
if (stats.errors.length > 0 || stats.warnings.length > 0) {
12911285
if (stats.warnings.length > 0) {
1292-
this.sendMessage(webSocketConnections, 'warnings', stats.warnings);
1286+
this.sendMessage(clients, 'warnings', stats.warnings);
12931287
}
12941288

12951289
if (stats.errors.length > 0) {
1296-
this.sendMessage(webSocketConnections, 'errors', stats.errors);
1290+
this.sendMessage(clients, 'errors', stats.errors);
12971291
}
12981292
} else {
1299-
this.sendMessage(webSocketConnections, 'ok');
1293+
this.sendMessage(clients, 'ok');
13001294
}
13011295
}
13021296

@@ -1337,7 +1331,13 @@ class Server {
13371331
// disabling refreshing on changing the content
13381332
if (this.options.liveReload) {
13391333
watcher.on('change', (item) => {
1340-
this.sendMessage(this.webSocketConnections, 'static-changed', item);
1334+
if (this.webSocketServer) {
1335+
this.sendMessage(
1336+
this.webSocketServer.clients,
1337+
'static-changed',
1338+
item
1339+
);
1340+
}
13411341
});
13421342
}
13431343

lib/servers/BaseServer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
module.exports = class BaseServer {
66
constructor(server) {
77
this.server = server;
8+
this.clients = new Set();
89
}
910
};

lib/servers/SockJSServer.js

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -61,37 +61,24 @@ module.exports = class SockJSServer extends BaseServer {
6161
...this.server.options.webSocketServer.options,
6262
prefix: getPrefix(this.server.options.webSocketServer.options),
6363
});
64-
}
6564

66-
send(connection, message) {
67-
// prevent cases where the server is trying to send data while connection is closing
68-
if (connection.readyState === 1) {
69-
connection.write(message);
70-
}
71-
}
65+
this.implementation.on('connection', (client) => {
66+
// Implement the the same API as for `ws`
67+
client.send = client.write;
7268

73-
close(callback) {
74-
[...this.server.webSocketConnections].forEach((socket) => {
75-
this.closeConnection(socket);
76-
});
77-
78-
if (callback) {
79-
callback();
80-
}
81-
}
69+
this.clients.add(client);
8270

83-
closeConnection(connection) {
84-
connection.close();
85-
}
86-
87-
// f should be passed the resulting connection and the connection headers
88-
onConnection(f) {
89-
this.implementation.on('connection', (connection) => {
90-
f(connection, connection ? connection.headers : null);
71+
client.on('close', () => {
72+
this.clients.delete(client);
73+
});
9174
});
92-
}
9375

94-
onConnectionClose(connection, f) {
95-
connection.on('close', f);
76+
this.implementation.close = () => {
77+
for (const client of this.clients) {
78+
client.close();
79+
}
80+
81+
this.clients.clear();
82+
};
9683
}
9784
};

lib/servers/WebsocketServer.js

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ const WebSocket = require('ws');
77
const BaseServer = require('./BaseServer');
88

99
module.exports = class WebsocketServer extends BaseServer {
10-
static heartbeatInterval = 30000;
10+
static heartbeatInterval = 1000;
1111

1212
constructor(server) {
1313
super(server);
1414

1515
this.implementation = new WebSocket.Server({
1616
...this.server.options.webSocketServer.options,
17+
clientTracking: false,
1718
noServer: true,
1819
});
1920

@@ -31,51 +32,37 @@ module.exports = class WebsocketServer extends BaseServer {
3132
this.server.logger.error(err.message);
3233
});
3334

34-
const noop = () => {};
35-
3635
const interval = setInterval(() => {
37-
this.implementation.clients.forEach((socket) => {
38-
if (socket.isAlive === false) {
39-
return socket.terminate();
36+
this.clients.forEach((client) => {
37+
if (client.isAlive === false) {
38+
client.terminate();
39+
40+
return;
4041
}
4142

42-
socket.isAlive = false;
43-
socket.ping(noop);
43+
client.isAlive = false;
44+
client.ping(() => {});
4445
});
4546
}, WebsocketServer.heartbeatInterval);
4647

47-
this.implementation.on('close', () => {
48-
clearInterval(interval);
49-
});
50-
}
51-
52-
send(connection, message) {
53-
// Prevent cases where the server is trying to send data while connection is closing
54-
if (connection.readyState === WebSocket.OPEN) {
55-
connection.send(message);
56-
}
57-
}
48+
this.implementation.on('connection', (client) => {
49+
this.clients.add(client);
5850

59-
close(callback) {
60-
this.implementation.close(callback);
61-
}
51+
client.isAlive = true;
6252

63-
closeConnection(connection) {
64-
connection.close();
65-
}
53+
client.on('pong', () => {
54+
client.isAlive = true;
55+
});
6656

67-
// f should be passed the resulting connection and the connection headers
68-
onConnection(f) {
69-
this.implementation.on('connection', (connection, req) => {
70-
connection.isAlive = true;
71-
connection.on('pong', () => {
72-
connection.isAlive = true;
57+
client.on('close', () => {
58+
this.clients.delete(client);
7359
});
74-
f(connection, req.headers);
7560
});
76-
}
7761

78-
onConnectionClose(connection, f) {
79-
connection.on('close', f);
62+
this.implementation.on('close', () => {
63+
clearInterval(interval);
64+
65+
this.clients.clear();
66+
});
8067
}
8168
};

test/e2e/web-socket-communication.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describe('web socket communication', () => {
6666

6767
await new Promise((resolve) => {
6868
const interval = setInterval(() => {
69-
if (server.webSocketConnections.length === 0) {
69+
if (server.webSocketServer.clients.size === 0) {
7070
clearInterval(interval);
7171
resolve();
7272
}
@@ -140,7 +140,7 @@ describe('web socket communication', () => {
140140
}, 200);
141141
});
142142

143-
expect(server.webSocketConnections).toHaveLength(0);
143+
expect(server.webSocketServer.clients.size).toBe(0);
144144
expect(consoleMessages.map((message) => message.text())).toMatchSnapshot(
145145
'console messages'
146146
);

0 commit comments

Comments
 (0)