From 3b98db62a2dd59a12da987f49bc970c62445737a Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Wed, 12 Feb 2020 13:57:40 -0500 Subject: [PATCH 1/2] initial additions required for channel content and the appropriate feedback to client upon all connection types (channel creation/addition/removal) --- src/channelManager.ts | 15 ++++++++++++--- src/channels/channelBase.ts | 7 +++++-- src/channels/types/customChannel.ts | 9 ++++----- src/channels/types/privateChannel.ts | 13 +++++++------ src/channels/types/publicChannel.ts | 8 ++++---- src/clients/clientBase.ts | 24 ++++++++++++------------ src/server.ts | 19 ++++++++++++++++++- 7 files changed, 62 insertions(+), 33 deletions(-) diff --git a/src/channelManager.ts b/src/channelManager.ts index a6100b1..42a58b9 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -47,11 +47,20 @@ class ChannelManager { if (channel) { channel.addClient(client); - client.connectToChannel(channel); return {status: 'success'}; } else { - logger.accessLog.info(`channel with id ${channel_id} does not exist.`); - return {status: 'notice', message: `channel with id ${channel_id} does not exist.`}; + logger.accessLog.info(`channel with id ${channel_id} does not exist. could not add client to channel`); + return {status: 'notice', message: `channel with id ${channel_id} does not exist. could not add client to channel`}; + } + } + + updateChannelContent(channel: PrivateChannel|PrivateChannel|CustomChannel, channelContent: JSON) { + if (channel) { + channel.channelContent = channelContent; + return {status: 'success'}; + } else { + logger.accessLog.info(`channel with id ${channel} does not exist. could not update content`); + return {status: 'notice', message: `channel with id ${channel} does not exist. could not update content`}; } } diff --git a/src/channels/channelBase.ts b/src/channels/channelBase.ts index 1bf3a06..b86716b 100644 --- a/src/channels/channelBase.ts +++ b/src/channels/channelBase.ts @@ -7,6 +7,8 @@ const logger = require('../logger'); class ChannelBase { id: string; clients: any[] = []; + broadcastConditions: string = "true"; + channelContent: JSON = JSON.parse('{}'); constructor(id: string) { this.id = id; @@ -26,8 +28,8 @@ class ChannelBase { return {status: 'success', message: `message broadcast complete`}; } - messageTransactionPossible(_from: PublicClient|PrivateClient|CustomClient, _to: PublicClient|PrivateClient|CustomClient) { - return true; + messageTransactionPossible(from: PublicClient|PrivateClient|CustomClient, to: PublicClient|PrivateClient|CustomClient) { + return eval(this.broadcastConditions); } addClient(client: PublicClient|PrivateClient|CustomClient) { @@ -36,6 +38,7 @@ class ChannelBase { return {status: 'notice', message: 'client already exists in channel'}; } else { this.clients.push(client); + client.connectToChannel(this); logger.accessLog.info('Added client to channel', {channelId: this.id, clientId: client.id}); return {status: 'success', message: 'client added'}; } diff --git a/src/channels/types/customChannel.ts b/src/channels/types/customChannel.ts index 82b6b47..fbb24f4 100644 --- a/src/channels/types/customChannel.ts +++ b/src/channels/types/customChannel.ts @@ -3,15 +3,14 @@ import ChannelBase from '../channelBase'; class CustomChannel extends ChannelBase { clients: CustomClient[] = []; - custom: any; constructor(id: string, custom: any) { super(id); - this.custom = custom; - } - messageTransactionPossible(from: CustomClient, to: CustomClient) { - return eval(this.custom.broadcastConditions); + if (custom) { + this.broadcastConditions = custom.broadcastConditions; + this.channelContent = custom.channelContent; + } } } diff --git a/src/channels/types/privateChannel.ts b/src/channels/types/privateChannel.ts index 98e5143..c4b742e 100644 --- a/src/channels/types/privateChannel.ts +++ b/src/channels/types/privateChannel.ts @@ -4,12 +4,13 @@ import ChannelBase from '../channelBase'; class PrivateChannel extends ChannelBase { clients: PrivateClient[] = []; - messageTransactionPossible(from: PrivateClient, to: PrivateClient) { - return ( - to !== from && - to.roles.includes('receiver') && - from.roles.includes('broadcaster') - ); + constructor(id: string) { + super(id); + + this.broadcastConditions = + "to != from && \ + to.roles.includes('receiver') && \ + from.roles.includes('broadcaster')"; } } diff --git a/src/channels/types/publicChannel.ts b/src/channels/types/publicChannel.ts index 2bdc2f0..3b7009e 100644 --- a/src/channels/types/publicChannel.ts +++ b/src/channels/types/publicChannel.ts @@ -4,10 +4,10 @@ import ChannelBase from '../channelBase'; class PublicChannel extends ChannelBase { clients: PublicClient[] = []; - messageTransactionPossible(from: PublicClient, to: PublicClient) { - return ( - to !== from - ); + constructor(id: string) { + super(id); + + this.broadcastConditions = "to != from"; } } diff --git a/src/clients/clientBase.ts b/src/clients/clientBase.ts index febb488..5d26f2e 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -17,6 +17,7 @@ class ClientBase { channelManager: ChannelManager; roles: string[]; messageListener: (data: any) => void; + closeListener: () => void; constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) { this.ws = ws; @@ -33,6 +34,16 @@ class ClientBase { this.messageTransaction(message); } }; + this.closeListener = () => { + logger.accessLog.info(`closing connection for client ${this.id}`); + + if (this.channel) { + this.channel.removeClient(this.id); + } + + this.clientManager.removeClient(this.id); + logger.accessLog.info(`closed connection for client ${this.id}`); + }; logger.accessLog.info('Client Created', {data}); } @@ -51,19 +62,8 @@ class ClientBase { connectToChannel(channel: PublicChannel|PrivateChannel|CustomChannel) { this.channel = channel; - this.ws.on('message', this.messageListener); - - this.ws.on('close', (reasonCode: string, description: string) => { - logger.accessLog.info(`closing connection for client ${this.id}`); - - if (this.channel) { - this.channel.removeClient(this.id); - } - - this.clientManager.removeClient(this.id); - logger.accessLog.info(`closed connection for client ${this.id}`); - }); + this.ws.on('close', this.closeListener); } messageTransaction(message: any) { diff --git a/src/server.ts b/src/server.ts index 3e2ae41..f0c4dfc 100755 --- a/src/server.ts +++ b/src/server.ts @@ -41,7 +41,24 @@ function connectionManager() { client = clientManager.addClient(data, channelManager, ws); } - if (client != null) channelManager.addClientToChannel(client, data.channel); + if (client != null) { + channelManager.addClientToChannel(client, data.channel); + + if (data.channel_type == 'custom') { + const channel = channelManager.channelExists(data.channel); + const connectionResponse = { + message_type: 'channelStatus', + content: channel.channelContent + } + + if (data.user_roles.includes('super')) { + channelManager.updateChannelContent(channel, data.custom.channelContent); + channel.broadcastMessage(connectionResponse); + } else { + client.directMessage(connectionResponse); + } + } + } logger.accessLog.info(`Purging empty channels...`); channelManager.purgeEmptyChannels(); From 030fb62b38f41a00d97cd29d2b63e38f8aeba144 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Thu, 13 Feb 2020 13:19:52 -0500 Subject: [PATCH 2/2] ping pong check, fix to channel status, explicit channel removal option added for custom channels --- src/channelManager.ts | 4 +++- src/channels/channelBase.ts | 1 + src/channels/types/customChannel.ts | 1 + src/clients/clientBase.ts | 10 ++++++++++ src/server.ts | 6 +++--- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/channelManager.ts b/src/channelManager.ts index 42a58b9..691355f 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -115,7 +115,9 @@ class ChannelManager { purgeEmptyChannels() { for (const channel of this.channels) { - if (channel.clients.length === 0 && channel.id !== 'default') { + logger.accessLog.info(`Purging empty channels...`); + + if (channel.clients.length === 0 && channel.explicitRemoval === false) { const index = this.channels.indexOf(channel); this.channels.splice(index, 1); logger.accessLog.info(`channel removed: ${channel.id}`); diff --git a/src/channels/channelBase.ts b/src/channels/channelBase.ts index b86716b..19ac2b2 100644 --- a/src/channels/channelBase.ts +++ b/src/channels/channelBase.ts @@ -9,6 +9,7 @@ class ChannelBase { clients: any[] = []; broadcastConditions: string = "true"; channelContent: JSON = JSON.parse('{}'); + explicitRemoval: boolean = false; constructor(id: string) { this.id = id; diff --git a/src/channels/types/customChannel.ts b/src/channels/types/customChannel.ts index fbb24f4..a87274f 100644 --- a/src/channels/types/customChannel.ts +++ b/src/channels/types/customChannel.ts @@ -10,6 +10,7 @@ class CustomChannel extends ChannelBase { if (custom) { this.broadcastConditions = custom.broadcastConditions; this.channelContent = custom.channelContent; + this.explicitRemoval = custom.explicitRemoval; } } } diff --git a/src/clients/clientBase.ts b/src/clients/clientBase.ts index 5d26f2e..4139ac7 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -18,6 +18,8 @@ class ClientBase { roles: string[]; messageListener: (data: any) => void; closeListener: () => void; + pongListener: () => void; + heartbeat: any; constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) { this.ws = ws; @@ -42,8 +44,14 @@ class ClientBase { } this.clientManager.removeClient(this.id); + clearInterval(this.heartbeat); logger.accessLog.info(`closed connection for client ${this.id}`); }; + this.pongListener = () => { + logger.accessLog.info(`client (${this.id}) ponged.`); + this.ws.pong(); + } + this.heartbeat = setInterval(() => { this.ws.ping('ping') }, 30000); logger.accessLog.info('Client Created', {data}); } @@ -64,6 +72,8 @@ class ClientBase { this.channel = channel; this.ws.on('message', this.messageListener); this.ws.on('close', this.closeListener); + this.ws.on('pong', this.pongListener); + this.heartbeat; } messageTransaction(message: any) { diff --git a/src/server.ts b/src/server.ts index f0c4dfc..43923b1 100755 --- a/src/server.ts +++ b/src/server.ts @@ -46,21 +46,21 @@ function connectionManager() { if (data.channel_type == 'custom') { const channel = channelManager.channelExists(data.channel); - const connectionResponse = { + let connectionResponse = { message_type: 'channelStatus', content: channel.channelContent } if (data.user_roles.includes('super')) { channelManager.updateChannelContent(channel, data.custom.channelContent); - channel.broadcastMessage(connectionResponse); + connectionResponse['content'] = channel.channelContent; + channel.broadcastMessage(client, connectionResponse); } else { client.directMessage(connectionResponse); } } } - logger.accessLog.info(`Purging empty channels...`); channelManager.purgeEmptyChannels(); ws.send(`Hi there, welcome to braid, Measures Web Socket server. Connecting all our services!\nYou are currently in channel: ${data.channel}`);