Add webRTC handlers
- Bind webRCT callbacks from socket
- Handle incoming account message for webRTC
GitLab: #53
Change-Id: I4ed28e7ca41e8e3870968819c75ffad249a188d0
diff --git a/server/src/app.ts b/server/src/app.ts
index f7c860a..4c1914b 100644
--- a/server/src/app.ts
+++ b/server/src/app.ts
@@ -22,6 +22,7 @@
import log from 'loglevel';
import { Service } from 'typedi';
+import { bindWebRTCCallbacks } from './handlers/webrtc-handler.js';
import { accountRouter } from './routers/account-router.js';
import { authRouter } from './routers/auth-router.js';
import { callRouter } from './routers/call-router.js';
@@ -49,6 +50,9 @@
app.use('/calls', callRouter);
app.use('/ns', nameserverRouter);
+ // Setup WebSocket callbacks
+ bindWebRTCCallbacks();
+
// Setup 404 error handling
app.use((_req, res) => {
res.sendStatus(HttpStatusCode.NotFound);
diff --git a/server/src/handlers/webrtc-handler.ts b/server/src/handlers/webrtc-handler.ts
new file mode 100644
index 0000000..10de56f
--- /dev/null
+++ b/server/src/handlers/webrtc-handler.ts
@@ -0,0 +1,40 @@
+/*
+ * 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 { AccountTextMessage, WebSocketMessage, WebSocketMessageType } from 'jami-web-common';
+import log from 'loglevel';
+import { Container } from 'typedi';
+
+import { Jamid } from '../jamid/jamid.js';
+import { Ws } from '../ws.js';
+
+const jamid = Container.get(Jamid);
+const ws = Container.get(Ws);
+
+function sendWebRTCData(message: Partial<WebSocketMessage>) {
+ const data: AccountTextMessage = message.data;
+ if (!data.from || !data.to || !data.message) {
+ log.warn('Incorrect format for AccountTextMessage (require from, to and message):', data);
+ return;
+ }
+ jamid.sendAccountTextMessage(data.from, data.to, JSON.stringify(message));
+}
+
+export function bindWebRTCCallbacks() {
+ ws.bind(WebSocketMessageType.WebRTCOffer, sendWebRTCData);
+ ws.bind(WebSocketMessageType.WebRTCAnswer, sendWebRTCData);
+}
diff --git a/server/src/jamid/jamid.ts b/server/src/jamid/jamid.ts
index 78cd71a..283e527 100644
--- a/server/src/jamid/jamid.ts
+++ b/server/src/jamid/jamid.ts
@@ -15,11 +15,12 @@
* License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
-import { AccountDetails, Message, VolatileDetails } from 'jami-web-common';
+import { AccountDetails, Message, VolatileDetails, WebSocketMessageType } from 'jami-web-common';
import log from 'loglevel';
import { filter, firstValueFrom, map, Subject } from 'rxjs';
import { Service } from 'typedi';
+import { Ws } from '../ws.js';
import { JamiSignal } from './jami-signal.js';
import {
AccountDetailsChanged,
@@ -46,7 +47,7 @@
private readonly usernamesToAccountIds: Map<string, string>;
private readonly events;
- constructor() {
+ constructor(private ws: Ws) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
this.jamiSwig = require('../../jamid.node') as JamiSwig; // TODO: we should put the path in the .env
@@ -134,8 +135,6 @@
// 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(): void {
@@ -186,10 +185,10 @@
return stringVectToArray(this.jamiSwig.getAccountList());
}
- sendAccountTextMessage(accountId: string, contactId: string, type: string, message: string): void {
+ sendAccountTextMessage(from: string, to: string, message: string): void {
const messageStringMap: StringMap = new this.jamiSwig.StringMap();
- messageStringMap.set(type, JSON.stringify(message));
- this.jamiSwig.sendAccountTextMessage(accountId, contactId, messageStringMap);
+ messageStringMap.set('application/json', message);
+ this.jamiSwig.sendAccountTextMessage(from, to, messageStringMap);
}
// TODO: Add interface for returned type
@@ -352,6 +351,22 @@
this.events.onIncomingAccountMessage.subscribe((signal) => {
log.debug('Received IncomingAccountMessage:', JSON.stringify(signal));
+ const message = JSON.parse(signal.message['application/json']);
+
+ if (message === undefined) {
+ log.debug('Undefined account message');
+ return;
+ }
+
+ if (!Object.values(WebSocketMessageType).includes(message.type)) {
+ log.warn(`Unhandled account message type: ${message.type}`);
+ return;
+ }
+
+ message.data.from = signal.from;
+ message.data.to = signal.accountId;
+ log.info(`Sending ${JSON.stringify(message)} to ${signal.accountId}`);
+ this.ws.send(signal.accountId, message);
});
this.events.onConversationReady.subscribe((signal) => {
diff --git a/server/src/routers/account-router.ts b/server/src/routers/account-router.ts
index c870f83..6ef7aa6 100644
--- a/server/src/routers/account-router.ts
+++ b/server/src/routers/account-router.ts
@@ -18,19 +18,12 @@
import { Request, Router } from 'express';
import asyncHandler from 'express-async-handler';
import { ParamsDictionary } from 'express-serve-static-core';
-import { AccountDetails, HttpStatusCode } from 'jami-web-common';
+import { AccountDetails, AccountTextMessage, HttpStatusCode } from 'jami-web-common';
import { Container } from 'typedi';
import { Jamid } from '../jamid/jamid.js';
import { authenticateToken } from '../middleware/auth.js';
-interface SendAccountTextMessageApi {
- from?: string;
- to?: string;
- type?: string;
- data?: string;
-}
-
const jamid = Container.get(Jamid);
export const accountRouter = Router();
@@ -76,16 +69,12 @@
res.sendStatus(HttpStatusCode.NoContent);
});
-accountRouter.post(
- '/send-account-message',
- (req: Request<ParamsDictionary, string, SendAccountTextMessageApi>, res) => {
- const { from, to, type, data } = req.body;
- if (!from || !to || !type || !data) {
- res.status(HttpStatusCode.BadRequest).send('Missing arguments in request');
- return;
- }
-
- jamid.sendAccountTextMessage(from, to, type, data);
- res.end();
+accountRouter.post('/send-account-message', (req: Request<ParamsDictionary, any, AccountTextMessage>, res) => {
+ const { from, to, message } = req.body;
+ if (!from || !to || !message) {
+ res.status(HttpStatusCode.BadRequest).send('Missing arguments in request');
+ return;
}
-);
+ jamid.sendAccountTextMessage(from, to, JSON.stringify(message));
+ res.end();
+});
diff --git a/server/src/ws.ts b/server/src/ws.ts
index 0081e59..58cd3db 100644
--- a/server/src/ws.ts
+++ b/server/src/ws.ts
@@ -51,6 +51,10 @@
ws.on('message', (messageString: string) => {
const message: WebSocketMessage = JSON.parse(messageString);
+ if (!message.type || !message.data) {
+ ws.send('Incorrect format (require type and data)');
+ return;
+ }
const callbacks = this.callbacks.get(message.type);
if (callbacks) {
for (const callback of callbacks) {