Add websocket logic server
- Websocket callback binding
- Websocket send message to specific connected socket
GitLab: #53
Change-Id: Ib52db5d99b0bc3ad4d1a775a62fecb9ea3b4d132
diff --git a/common/src/enums/websocket-message-type.ts b/common/src/enums/websocket-message-type.ts
new file mode 100644
index 0000000..9f95d60
--- /dev/null
+++ b/common/src/enums/websocket-message-type.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+export enum WebSocketMessageType {
+ WebRTCOffer = 'webrtc-offer',
+ WebRCTAnswer = 'webrtc-answer',
+}
diff --git a/common/src/index.ts b/common/src/index.ts
index 41e19b3..8dbab24 100644
--- a/common/src/index.ts
+++ b/common/src/index.ts
@@ -20,4 +20,6 @@
export * from './Contact.js';
export * from './Conversation.js';
export * from './enums/http-status-code.js';
+export * from './enums/websocket-message-type.js';
+export * from './interfaces/websocket-message.js';
export * from './util.js';
diff --git a/common/src/interfaces/websocket-message.ts b/common/src/interfaces/websocket-message.ts
new file mode 100644
index 0000000..f42b67b
--- /dev/null
+++ b/common/src/interfaces/websocket-message.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+import { WebSocketMessageType } from '../enums/websocket-message-type';
+
+export interface WebSocketMessage {
+ type: WebSocketMessageType;
+ data: any;
+}
diff --git a/server/src/jamid/jamid.ts b/server/src/jamid/jamid.ts
index 27ff0e3..ee91ad4 100644
--- a/server/src/jamid/jamid.ts
+++ b/server/src/jamid/jamid.ts
@@ -138,6 +138,8 @@
// 2. You cannot specify multiple handlers for the same event
// 3. You cannot specify a default handler
this.jamiSwig.init(handlers);
+
+ // TODO: Bind websocket callbacks for webrtc action on Incoming account message
}
stop() {
@@ -189,7 +191,7 @@
sendAccountTextMessage(accountId: string, contactId: string, type: string, message: string) {
const messageStringMap: StringMap = new this.jamiSwig.StringMap();
- messageStringMap.set(type, message);
+ messageStringMap.set(type, JSON.stringify(message));
this.jamiSwig.sendAccountTextMessage(accountId, contactId, messageStringMap);
}
@@ -313,7 +315,7 @@
this.events.onMessageReceived.subscribe((signal) => {
log.debug('Received MessageReceived:', JSON.stringify(signal));
- // TODO: Send message to client using WebSocket
+ // TODO: Send message to client using WS service
});
}
}
diff --git a/server/src/ws.ts b/server/src/ws.ts
index eadbab0..0081e59 100644
--- a/server/src/ws.ts
+++ b/server/src/ws.ts
@@ -18,6 +18,7 @@
import { IncomingMessage } from 'node:http';
import { Duplex } from 'node:stream';
+import { WebSocketMessage, WebSocketMessageType } from 'jami-web-common';
import { jwtVerify } from 'jose';
import log from 'loglevel';
import { Service } from 'typedi';
@@ -28,16 +29,34 @@
@Service()
export class Ws {
- constructor(private readonly vault: Vault) {}
+ private sockets: Map<string, WebSocket[]>;
+ private callbacks: Map<WebSocketMessageType, ((message: WebSocketMessage) => void)[]>;
+
+ constructor(private readonly vault: Vault) {
+ this.sockets = new Map();
+ this.callbacks = new Map();
+ }
async build() {
const wss = new WebSocketServer({ noServer: true });
+
wss.on('connection', (ws: WebSocket, _req: IncomingMessage, accountId: string) => {
log.info('New connection', accountId);
- // TODO: Add the account ID here to a map of accountId -> WebSocket connections
+ const accountSockets = this.sockets.get(accountId);
+ if (accountSockets) {
+ accountSockets.push(ws);
+ } else {
+ this.sockets.set(accountId, [ws]);
+ }
- ws.on('message', (_data) => {
- ws.send(JSON.stringify({ accountId }));
+ ws.on('message', (messageString: string) => {
+ const message: WebSocketMessage = JSON.parse(messageString);
+ const callbacks = this.callbacks.get(message.type);
+ if (callbacks) {
+ for (const callback of callbacks) {
+ callback(message);
+ }
+ }
});
});
@@ -67,4 +86,24 @@
});
};
}
+
+ bind(messageType: WebSocketMessageType, callback: (message: WebSocketMessage) => void): void {
+ const messageTypeCallbacks = this.callbacks.get(messageType);
+ if (messageTypeCallbacks) {
+ messageTypeCallbacks.push(callback);
+ } else {
+ this.callbacks.set(messageType, [callback]);
+ }
+ }
+
+ send(accountId: string, message: WebSocketMessage): boolean {
+ const accountSockets = this.sockets.get(accountId);
+ if (!accountSockets) {
+ return false;
+ }
+ for (const accountSocket of accountSockets) {
+ accountSocket.send(message);
+ }
+ return true;
+ }
}