You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

107 lines
2.5 KiB
JavaScript

'use strict';
const createEventEmitter = require("create-event-emitter");
const defaultValue = require("default-value");
function removeFromArray(array, item) {
let index = array.indexOf(item);
if (index !== -1) {
array.splice(index, 1);
}
}
module.exports = function createClientStore(options = {}) {
let clients = [];
let subscribedClients = [];
let store = createEventEmitter({
add: function addClient(client) {
let clientObject = createEventEmitter({
socket: client,
subscribed: false,
disconnected: false,
kill: function killClient() {
store.kill(client);
},
send: function sendMessage(message) {
let data = JSON.stringify(message);
this.socket.send(data, (error) => {
if (error != null) {
if (error.message === "not opened") {
/* The socket has disappeared. We can safely ignore this. Is there a more reliable way to detect this error than by its message, though? */
} else {
store.emit("error", error);
}
}
});
}
});
client.on("message", (data) => {
let message;
try {
message = JSON.parse(data);
} catch (err) {
/* User sent invalid data; disconnect them. */
return this.kill(client);
}
if (message == null || typeof message !== "object") {
/* User sent invalid data; disconnect them. */
return this.kill(client);
}
if (message.type === "subscribe") {
if (clientObject.subscribed === false) {
subscribedClients.push(clientObject);
clientObject.subscribed = true;
clientObject.emit("subscribed");
}
} else {
clientObject.emit("message", message);
}
});
client.on("close", (code, reason) => {
client.disconnected = true;
this.remove(clientObject);
});
clients.push(clientObject);
return clientObject;
},
kill: function killClient(client) {
client.close();
},
remove: function removeClient(client) {
removeFromArray(clients, client);
removeFromArray(subscribedClients, client);
},
broadcast: function broadcastMessage(message, clients) {
defaultValue(clients, subscribedClients).forEach((client) => {
client.send(message);
});
},
broadcastAll: function broadcastMessageToAll(message) {
this.broadcast(message, clients);
}
});
let pingInterval = defaultValue(options.pingInterval, 5000);
function pingClients() {
store.broadcastAll({
type: "ping"
});
setTimeout(pingClients, pingInterval);
}
pingClients();
return store;
};