Skip to content

Commit 1a7c827

Browse files
authoredJan 31, 2020
fix: add heartbeat for the websocket server (#2404)
1 parent 0325b01 commit 1a7c827

File tree

4 files changed

+91
-50
lines changed

4 files changed

+91
-50
lines changed
 

‎lib/Server.js

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class Server {
7171

7272
updateCompiler(this.compiler, this.options);
7373

74+
this.heartbeatInterval = 30000;
7475
// this.SocketServerImplementation is a class, so it must be instantiated before use
7576
this.socketServerImplementation = getSocketServerImplementation(
7677
this.options

‎lib/servers/WebsocketServer.js

+15
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ module.exports = class WebsocketServer extends BaseServer {
2727
this.wsServer.on('error', (err) => {
2828
this.server.log.error(err.message);
2929
});
30+
31+
const noop = () => {};
32+
33+
setInterval(() => {
34+
this.wsServer.clients.forEach((ws) => {
35+
if (ws.isAlive === false) return ws.terminate();
36+
37+
ws.isAlive = false;
38+
ws.ping(noop);
39+
});
40+
}, this.server.heartbeatInterval);
3041
}
3142

3243
send(connection, message) {
@@ -45,6 +56,10 @@ module.exports = class WebsocketServer extends BaseServer {
4556
// f should be passed the resulting connection and the connection headers
4657
onConnection(f) {
4758
this.wsServer.on('connection', (connection, req) => {
59+
connection.isAlive = true;
60+
connection.on('pong', () => {
61+
connection.isAlive = true;
62+
});
4863
f(connection, req.headers);
4964
});
5065
}

‎test/server/servers/WebsocketServer.test.js

+72-48
Original file line numberDiff line numberDiff line change
@@ -7,87 +7,111 @@ const WebsocketServer = require('../../../lib/servers/WebsocketServer');
77
const port = require('../../ports-map').WebsocketServer;
88

99
describe('WebsocketServer', () => {
10+
let server;
1011
let socketServer;
1112
let listeningApp;
1213

13-
beforeAll((done) => {
14+
beforeEach((done) => {
1415
// eslint-disable-next-line new-cap
1516
const app = new express();
1617

1718
listeningApp = http.createServer(app);
1819
listeningApp.listen(port, 'localhost', () => {
19-
const server = {
20+
server = {
2021
log: {
2122
error: () => {},
2223
debug: () => {},
2324
},
2425
sockPath: '/ws-server',
2526
listeningApp,
27+
heartbeatInterval: 800,
2628
};
27-
2829
socketServer = new WebsocketServer(server);
29-
3030
done();
3131
});
3232
});
3333

34-
describe('server', () => {
35-
it('should recieve connection, send message, and close client', (done) => {
36-
const data = [];
37-
38-
let headers;
39-
socketServer.onConnection((connection, h) => {
40-
headers = h;
41-
data.push('open');
42-
socketServer.send(connection, 'hello world');
43-
setTimeout(() => {
44-
// the server closes the connection with the client
45-
socketServer.close(connection);
46-
}, 1000);
47-
});
34+
it('should recieve connection, send message, and close client', (done) => {
35+
const data = [];
4836

49-
// eslint-disable-next-line new-cap
50-
const client = new ws(`http://localhost:${port}/ws-server`);
37+
let headers;
38+
socketServer.onConnection((connection, h) => {
39+
headers = h;
40+
data.push('open');
41+
socketServer.send(connection, 'hello world');
42+
setTimeout(() => {
43+
// the server closes the connection with the client
44+
socketServer.close(connection);
45+
}, 1000);
46+
});
5147

52-
client.onmessage = (e) => {
53-
data.push(e.data);
54-
};
48+
// eslint-disable-next-line new-cap
49+
const client = new ws(`http://localhost:${port}/ws-server`);
5550

56-
client.onclose = () => {
57-
data.push('close');
58-
};
51+
client.onmessage = (e) => {
52+
data.push(e.data);
53+
};
5954

60-
setTimeout(() => {
61-
expect(headers.host).toMatchSnapshot();
62-
expect(data).toMatchSnapshot();
63-
done();
64-
}, 3000);
55+
client.onclose = () => {
56+
data.push('close');
57+
};
58+
59+
// the heartbeat interval was shortened greatly above
60+
// so that the client is quickly pinged
61+
client.on('ping', () => {
62+
data.push('ping');
6563
});
6664

67-
it('should receive client close event', (done) => {
68-
let receivedClientClose = false;
69-
socketServer.onConnection((connection) => {
70-
socketServer.onConnectionClose(connection, () => {
71-
receivedClientClose = true;
72-
});
65+
setTimeout(() => {
66+
expect(headers.host).toMatchSnapshot();
67+
expect(data).toMatchSnapshot();
68+
done();
69+
}, 3000);
70+
});
71+
72+
it('should receive client close event', (done) => {
73+
let receivedClientClose = false;
74+
socketServer.onConnection((connection) => {
75+
socketServer.onConnectionClose(connection, () => {
76+
receivedClientClose = true;
7377
});
78+
});
7479

75-
// eslint-disable-next-line new-cap
76-
const client = new ws(`http://localhost:${port}/ws-server`);
80+
// eslint-disable-next-line new-cap
81+
const client = new ws(`http://localhost:${port}/ws-server`);
7782

78-
setTimeout(() => {
79-
// the client closes itself, the server does not close it
80-
client.close();
81-
}, 1000);
83+
setTimeout(() => {
84+
// the client closes itself, the server does not close it
85+
client.close();
86+
}, 1000);
8287

83-
setTimeout(() => {
84-
expect(receivedClientClose).toBeTruthy();
85-
done();
86-
}, 3000);
88+
setTimeout(() => {
89+
expect(receivedClientClose).toBeTruthy();
90+
done();
91+
}, 3000);
92+
});
93+
94+
it('should terminate a client that is not alive', (done) => {
95+
let receivedClientClose = false;
96+
socketServer.onConnection((connection) => {
97+
// this makes the server think the client did not respond
98+
// to a ping in time, so the server will terminate it
99+
connection.isAlive = false;
100+
socketServer.onConnectionClose(connection, () => {
101+
receivedClientClose = true;
102+
});
87103
});
104+
105+
// eslint-disable-next-line new-cap, no-unused-vars
106+
const client = new ws(`http://localhost:${port}/ws-server`);
107+
108+
setTimeout(() => {
109+
expect(receivedClientClose).toBeTruthy();
110+
done();
111+
}, 3000);
88112
});
89113

90-
afterAll((done) => {
114+
afterEach((done) => {
91115
listeningApp.close(done);
92116
});
93117
});
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`WebsocketServer server should recieve connection, send message, and close client 1`] = `"localhost:8131"`;
3+
exports[`WebsocketServer should recieve connection, send message, and close client 1`] = `"localhost:8131"`;
44

5-
exports[`WebsocketServer server should recieve connection, send message, and close client 2`] = `
5+
exports[`WebsocketServer should recieve connection, send message, and close client 2`] = `
66
Array [
77
"open",
88
"hello world",
9+
"ping",
910
"close",
1011
]
1112
`;

0 commit comments

Comments
 (0)
Please sign in to comment.