From c044daf366a7a6d55afe7ffea3d9fb13bc75122e Mon Sep 17 00:00:00 2001 From: Hunseop Jeong Date: Tue, 26 Apr 2022 16:50:50 +0900 Subject: [PATCH] [SignalingServer] Enable the d2d_offload flag Update the signaling server code and define the service type for the signaling server to run the service independently. Change-Id: Ib69f48392b00734231a8f0f2c35f0feb87f75c12 Signed-off-by: Hunseop Jeong --- device_home/signaling_server/gen/app.js | 377 +-------------------- device_home/signaling_server/gen/edge.js | 6 +- device_home/signaling_server/gen/service.js | 409 +++++++++++++++++++++++ device_home/signaling_server/gen/socket-tizen.js | 2 +- device_home/signaling_server/gen/util.js | 4 +- packaging/config.xml.in | 5 + packaging/wrtjs.spec | 1 + 7 files changed, 423 insertions(+), 381 deletions(-) create mode 100644 device_home/signaling_server/gen/service.js diff --git a/device_home/signaling_server/gen/app.js b/device_home/signaling_server/gen/app.js index 44998f0..8b0a0a8 100644 --- a/device_home/signaling_server/gen/app.js +++ b/device_home/signaling_server/gen/app.js @@ -1,376 +1,3 @@ -const path = require('path'); -const fs = require('fs'); -const express = require('express'); -const QRCode = require('qrcode'); -const Edge = require('./edge'); -const SocketTizen = require('./socket-tizen'); -const { getMyAddress } = require('./util'); +const { onStart } = require('./service'); -const TAG = 'app.js'; - -const app = express(); - -const options = { - key: fs.readFileSync(path.resolve(__dirname, 'key.pem')), - cert: fs.readFileSync(path.resolve(__dirname, 'cert.pem')) -}; - -console.log(TAG, `platform : ${process.platform}`); - -const httpPort = process.env.HTTP_PORT || 9559; -const httpsPort = process.env.PORT || process.env.HTTPS_PORT || 5443; -const httpsServer = require('https').createServer(options, app); -const httpServer = require('http').createServer(app); -const io = require('socket.io')(); -const isTizen = process.platform === 'tizen'; -const supportMessagePort = isTizen; -// Implementation for edge orchestration -const supportEdgeOrchestration = - typeof webapis !== 'undefined' && webapis.hasOwnProperty('edge'); -console.log(TAG, `supportEdgeOrchestration : ${supportEdgeOrchestration}`); - -io.attach(httpServer); -io.attach(httpsServer); - -const clients = new Set(); -const workers = new Map(); -const sockets = new Map(); -let edgeForCastanets = null; -let forceQuitTimer = null; -let isMeerkatStarted = false; - -app.set('host', '0.0.0.0'); - -if (isTizen) { - app.use(express.static(path.join(__dirname, './public'))); -} else { - app.use( - '/offload.html', - express.static(path.join(__dirname, '../../sample/offload.html')) - ); - // Host offload-worker - app.use( - '/offload-worker', - express.static(path.join(__dirname, '../../offload-worker/src/')) - ); - app.use( - '/offload-worker/offload-worker.js', - express.static(path.join(__dirname, '../../build/offload-worker.js')) - ); - const serveIndex = require('serve-index'); - app.use( - '/', - express.static(path.join(__dirname, '../../build')), - express.static(path.join(__dirname, '../../sample')), - serveIndex(path.join(__dirname, '../../sample')) - ); - app.use( - '/test', - express.static(path.join(__dirname, '../../test')), - serveIndex(path.join(__dirname, '../../test')) - ); -} - -function onConnection(socket) { - if (isTizen && !isMeerkatStarted) { - try { - console.log(TAG, `Try to start Meerkat client.`); - tizen.application.launch( - 'org.tizen.meerkat.client', - () => { - console.log(TAG, `Meerkat client is started.`); - isMeerkatStarted = true; - }, - err => console.error(TAG, 'Failed to launch Meerkat client. ' + err) - ); - } catch (err) { - console.error(TAG, 'Failed to launch Meerkat client. ' + err); - } - } - console.log(TAG, `connection from '${socket.id}.`); - sockets.set(socket.id, socket); - - // client creates a session. - socket.on('create', async function () { - if (forceQuitTimer !== null) { - clearTimeout(forceQuitTimer); - } - - if (clients.has(socket.id)) { - console.log(TAG, `already created by ${socket.id}.`); - return; - } - clients.add(socket.id); - - let qr = null; - const myAddress = getMyAddress(); - if (myAddress) { - try { - qr = await QRCode.toDataURL( - 'https://' + myAddress + ':5443/offload-worker.html' - ); - } catch (err) { - console.error(TAG, 'unabled to generate QR: ' + error); - } - } - - socket.emit('greeting', { - qrCode: qr, - workers: Array.from(workers) - }); - - console.log( - TAG, - `[client] session created by ${socket.id}. workers.size : ${workers.size}` - ); - - if (supportEdgeOrchestration) { - socket.emit( - 'capabilities', - Array.from(edgeForCastanets.getCapabilities()) - ); - } - }); - - socket.on('getcapabilities', function () { - console.log(TAG, `getcapabilities`); - if (supportEdgeOrchestration) { - socket.emit( - 'capabilities', - Array.from(edgeForCastanets.getCapabilities()) - ); - } else { - socket.emit('capabilities', []); - } - }); - - socket.on('requestService', function (workerId) { - if (supportEdgeOrchestration) { - edgeForCastanets.requestService(workerId); - } - }); - - // new worker has been joined. - socket.on('join', function (worker) { - if (supportEdgeOrchestration) { - let deviceIp = socket.request.connection.remoteAddress; - if (deviceIp.indexOf('::ffff:') !== -1) { - deviceIp = deviceIp.substr(7, deviceIp.length); - } - - if (deviceIp) { - edgeForCastanets.joinDevice(deviceIp); - } - } - - workers.set(worker.id, { - socketId: socket.id, - name: worker.name, - features: worker.features, - mediaDeviceInfos: worker.mediaDeviceInfos, - compute_tasks: 0 - }); - console.log( - TAG, - `worker[${workers.size}] join: '${worker.id}' - '${socket.id}', '${worker.name}'`, - worker.features - ); - - for (const client of clients) { - const clientSocket = sockets.get(client); - clientSocket.emit('worker', { - event: 'join', - workerId: worker.id, - socketId: socket.id, - name: worker.name, - features: worker.features, - mediaDeviceInfos: worker.mediaDeviceInfos - }); - } - }); - - // route message between clients. - socket.on('message', function (data) { - console.log(TAG, `message ${JSON.stringify(data)}`); - let socketId = null; - if (workers.has(data.to)) { - socketId = workers.get(data.to).socketId; - } else if (clients.has(data.to)) { - socketId = data.to; - } - - if (socketId) { - const socket = sockets.get(socketId); - socket.emit('message', data); - } - }); - - socket.on('disconnect', function (reason) { - const socketId = socket.id; - sockets.delete(socketId); - if (clients.has(socketId)) { - console.log(TAG, `[client] session terminated by client: ${socketId}`); - - // broadcast to offload-worker - for (const socket of sockets.values()) { - socket.emit('client', { - event: 'bye', - socketId: socketId - }); - } - clients.delete(socketId); - - if (clients.size === 0) { - if (supportMessagePort) { - closeServer(); - } - forceQuitTimer = setTimeout(function () { - console.log( - TAG, - `All clients are destroyed. Broadcast 'forceQuit' to workers` - ); - for (const socket of sockets.values()) { - socket.emit('client', { - event: 'forceQuit', - socketId: socketId - }); - } - }, 5000); - } - } else { - if (supportEdgeOrchestration) { - let deviceIp = socket.request.connection.remoteAddress; - if (deviceIp.indexOf('::ffff:') !== -1) { - deviceIp = deviceIp.substr(7, deviceIp.length); - } - - if (deviceIp) { - edgeForCastanets.disconnectDevice(deviceIp); - } - } - - let workerId = null; - workers.forEach(function (value, key, map) { - if (value.socketId === socket.id) { - workerId = key; - } - }); - - if (workerId) { - for (const client of clients) { - const socket = sockets.get(client); - socket.emit('worker', { - event: 'bye', - workerId: workerId, - socketId: socket.id - }); - } - workers.delete(workerId); - console.log(TAG, `worker[${workers.size}] bye: '${workerId}'`); - } - } - }); -} - -io.of('/offload-js').on('connection', onConnection); - -if (supportEdgeOrchestration) { - edgeForCastanets = new Edge( - 'castanets', - 'android', - 'com.samsung.android.castanets' - ); -} - -function startServer() { - console.log(TAG, 'starting server...'); - - if (!httpsServer.listening) { - httpsServer.listen(httpsPort, function () { - console.log(TAG, `server is listening on https ${httpsPort} port.`); - }); - } - - if (!httpServer.listening) { - httpServer.listen(httpPort, function () { - console.log(TAG, `server is listening on http ${httpPort} port.`); - }); - } -} - -function closeServer() { - console.log(TAG, 'closing server...'); - - if (httpsServer.listening) { - httpsServer.close(err => { - if (err) { - console.error(`failed to close the https server:`, err); - } - }); - } - - if (httpServer.listening) { - httpServer.close(err => { - if (err) { - console.error(`failed to close the http server:`, err); - } - }); - } -} - -if (supportMessagePort) { - console.log(TAG, 'listening tizen messageport...'); - const localPort = tizen.messageport.requestLocalMessagePort('offload'); - localPort.addMessagePortListener(messages => { - if (messages.length === 0) { - console.error(TAG, 'Not found message'); - return; - } - - const message = messages[0]; - const event = message.key; - const value = JSON.parse(message.value); - const id = value.id; - - if (event === 'connect') { - // FIXME: The message port does not guarantee that the connection has - // been disconnected when the page is reloaded. Therefore, if a new - // connection occurs with the same id, the existing connection is - // disconnected. - if (sockets.has(id)) { - console.log(TAG, `Disconnect already connected socket: ${id}`); - const socket = sockets.get(id); - socket.handleEvents('disconnect'); - } - - const socket = new SocketTizen(id, localPort); - socket.on('connection', onConnection); - socket.connect(); - sockets.set(id, socket); - startServer(); - } else { - const socket = sockets.get(id); - socket.handleEvents(event, value.data); - } - }); - - // Check the client status - function checkConnectionStatus() { - for (const client of clients) { - const socket = sockets.get(client); - if (socket.constructor === SocketTizen) { - try { - socket.emit('status'); - } catch (e) { - console.error(TAG, `Failed to check ${client} status`); - socket.handleEvents('disconnect'); - } - } - } - } - - // Prevent to terminate the process - setInterval(checkConnectionStatus, 1000); -} else { - startServer(); -} +onStart(); diff --git a/device_home/signaling_server/gen/edge.js b/device_home/signaling_server/gen/edge.js index 8573ef2..4751384 100644 --- a/device_home/signaling_server/gen/edge.js +++ b/device_home/signaling_server/gen/edge.js @@ -43,7 +43,7 @@ class Edge { ); if (deviceList === null || deviceList.ipaddrs.length === 0) { - console.log(TAG, `deviceList is null`); + console.log(TAG, 'deviceList is null'); return this._capabilities; } @@ -57,8 +57,8 @@ class Edge { console.log(TAG, `ReadCapability : ${ipaddr}, ${features.capability}`); try { let jsonCapability = JSON.parse(features.capability); - if (jsonCapability.hasOwnProperty('offloadjs')) { - jsonCapability = jsonCapability['offloadjs']; + if (Object.hasOwnProperty.call(jsonCapability, 'offloadjs')) { + jsonCapability = jsonCapability.offloadjs; } this._capabilities.set(jsonCapability.id, { ipaddr: ipaddr, diff --git a/device_home/signaling_server/gen/service.js b/device_home/signaling_server/gen/service.js new file mode 100644 index 0000000..86d51bb --- /dev/null +++ b/device_home/signaling_server/gen/service.js @@ -0,0 +1,409 @@ +const path = require('path'); +const fs = require('fs'); +const express = require('express'); +const QRCode = require('qrcode'); +const Edge = require('./edge'); +const SocketTizen = require('./socket-tizen'); +const { getMyAddress } = require('./util'); + +const TAG = 'service.js'; + +const app = express(); + +const options = { + key: fs.readFileSync(path.resolve(__dirname, 'key.pem')), + cert: fs.readFileSync(path.resolve(__dirname, 'cert.pem')) +}; + +console.log(TAG, `platform : ${process.platform}`); + +const httpPort = process.env.HTTP_PORT || 9559; +const httpsPort = process.env.PORT || process.env.HTTPS_PORT || 5443; +const httpsServer = require('https').createServer(options, app); +const httpServer = require('http').createServer(app); +const io = require('socket.io')(); +const isTizen = process.platform === 'tizen'; +const supportMessagePort = isTizen; +// Implementation for edge orchestration +const supportEdgeOrchestration = + typeof webapis !== 'undefined' && Object.hasOwnProperty.call(webapis, 'edge'); +console.log(TAG, `supportEdgeOrchestration : ${supportEdgeOrchestration}`); + +io.attach(httpServer); +io.attach(httpsServer); + +const clients = new Set(); +const workers = new Map(); +const sockets = new Map(); +let edgeForCastanets = null; +let forceQuitTimer = null; +let isMeerkatStarted = false; +let statusIntervalId = null; +let localPort = null; + +app.set('host', '0.0.0.0'); + +if (isTizen) { + app.use(express.static(path.join(__dirname, './public'))); + app.use(express.static(path.join(__dirname, '../../shared/res'))); +} else { + app.use( + '/offload.html', + express.static(path.join(__dirname, '../../offload/apps/web/offload.html')) + ); + // Host offload-worker + app.use( + '/offload-worker', + express.static(path.join(__dirname, '../../offload-worker/apps/web/')) + ); + app.use( + '/offload-worker/offload-worker.js', + express.static(path.join(__dirname, '../../dist/offload-worker.js')) + ); + app.use( + '/face.webm', + express.static(path.join(__dirname, '../../offload-worker/apps/web/face.webm')) + ); + const serveIndex = require('serve-index'); + app.use( + '/', + express.static(path.join(__dirname, '../../dist')), + express.static(path.join(__dirname, '../../offload/sample')), + serveIndex(path.join(__dirname, '../../offload/sample')) + ); + app.use( + '/test', + express.static(path.join(__dirname, '../../test')), + serveIndex(path.join(__dirname, '../../test')) + ); +} + +function onConnection(socket) { + if (isTizen && !isMeerkatStarted) { + try { + console.log(TAG, 'Try to start Meerkat client.'); + tizen.application.launch( + 'org.tizen.meerkat.client', + () => { + console.log(TAG, 'Meerkat client is started.'); + isMeerkatStarted = true; + }, + err => console.error(TAG, 'Failed to launch Meerkat client. ' + err) + ); + } catch (err) { + console.error(TAG, 'Failed to launch Meerkat client. ' + err); + } + } + console.log(TAG, `connection from '${socket.id}.`); + sockets.set(socket.id, socket); + + // client creates a session. + socket.on('create', async function () { + if (forceQuitTimer !== null) { + clearTimeout(forceQuitTimer); + } + + if (clients.has(socket.id)) { + console.log(TAG, `already created by ${socket.id}.`); + return; + } + clients.add(socket.id); + + let qr = null; + const myAddress = getMyAddress(); + if (myAddress) { + try { + qr = await QRCode.toDataURL( + 'https://' + myAddress + ':5443/offload-worker.html' + ); + } catch (err) { + console.error(TAG, 'unabled to generate QR: ' + err); + } + } + + socket.emit('greeting', { + qrCode: qr, + workers: Array.from(workers) + }); + + console.log( + TAG, + `[client] session created by ${socket.id}. workers.size : ${workers.size}` + ); + + if (supportEdgeOrchestration) { + socket.emit( + 'capabilities', + Array.from(edgeForCastanets.getCapabilities()) + ); + } + }); + + socket.on('getcapabilities', function () { + console.log(TAG, 'getcapabilities'); + if (supportEdgeOrchestration) { + socket.emit( + 'capabilities', + Array.from(edgeForCastanets.getCapabilities()) + ); + } else { + socket.emit('capabilities', []); + } + }); + + socket.on('requestService', function (workerId) { + if (supportEdgeOrchestration) { + edgeForCastanets.requestService(workerId); + } + }); + + // new worker has been joined. + socket.on('join', function (worker) { + if (supportEdgeOrchestration) { + let deviceIp = socket.request.connection.remoteAddress; + if (deviceIp.indexOf('::ffff:') !== -1) { + deviceIp = deviceIp.substr(7, deviceIp.length); + } + + if (deviceIp) { + edgeForCastanets.joinDevice(deviceIp); + } + } + + workers.set(worker.id, { + socketId: socket.id, + name: worker.name, + features: worker.features, + mediaDeviceInfos: worker.mediaDeviceInfos, + compute_tasks: 0 + }); + console.log( + TAG, + `worker[${workers.size}] join: '${worker.id}' - '${socket.id}', '${worker.name}'`, + worker.features + ); + + for (const client of clients) { + const clientSocket = sockets.get(client); + clientSocket.emit('worker', { + event: 'join', + workerId: worker.id, + socketId: socket.id, + name: worker.name, + features: worker.features, + mediaDeviceInfos: worker.mediaDeviceInfos + }); + } + }); + + // route message between clients. + socket.on('message', function (data) { + console.log(TAG, `message ${JSON.stringify(data)}`); + let socketId = null; + if (workers.has(data.to)) { + socketId = workers.get(data.to).socketId; + } else if (clients.has(data.to)) { + socketId = data.to; + } + + if (socketId) { + const socket = sockets.get(socketId); + socket.emit('message', data); + } + }); + + socket.on('disconnect', function (reason) { + const socketId = socket.id; + sockets.delete(socketId); + if (clients.has(socketId)) { + console.log(TAG, `[client] session terminated by client: ${socketId}`); + clients.delete(socketId); + + // broadcast to offload-worker + for (const socket of sockets.values()) { + socket.emit('client', { + event: 'bye', + socketId: socketId + }); + } + + if (clients.size === 0) { + if (supportMessagePort) { + closeServer(); + } + forceQuitTimer = setTimeout(function () { + console.log( + TAG, + 'All clients are destroyed. Broadcast \'forceQuit\' to workers' + ); + for (const socket of sockets.values()) { + socket.emit('client', { + event: 'forceQuit', + socketId: socketId + }); + } + }, 5000); + } + } else { + if (supportEdgeOrchestration) { + let deviceIp = socket.request.connection.remoteAddress; + if (deviceIp.indexOf('::ffff:') !== -1) { + deviceIp = deviceIp.substr(7, deviceIp.length); + } + + if (deviceIp) { + edgeForCastanets.disconnectDevice(deviceIp); + } + } + + let workerId = null; + workers.forEach(function (value, key, map) { + if (value.socketId === socket.id) { + workerId = key; + } + }); + + if (workerId) { + for (const client of clients) { + const socket = sockets.get(client); + socket.emit('worker', { + event: 'bye', + workerId: workerId, + socketId: socket.id + }); + } + workers.delete(workerId); + console.log(TAG, `worker[${workers.size}] bye: '${workerId}'`); + } + } + }); +} + +io.of('/offload-js').on('connection', onConnection); + +if (supportEdgeOrchestration) { + edgeForCastanets = new Edge( + 'castanets', + 'android', + 'com.samsung.android.castanets' + ); +} + +function startServer() { + console.log(TAG, 'starting server...'); + + if (!httpsServer.listening) { + httpsServer.listen(httpsPort, function () { + console.log(TAG, `server is listening on https ${httpsPort} port.`); + }); + } + + if (!httpServer.listening) { + httpServer.listen(httpPort, function () { + console.log(TAG, `server is listening on http ${httpPort} port.`); + }); + } +} + +function closeServer() { + console.log(TAG, 'closing server...'); + + if (httpsServer.listening) { + httpsServer.close(err => { + if (err) { + console.error('failed to close the https server:', err); + } + }); + } + + if (httpServer.listening) { + httpServer.close(err => { + if (err) { + console.error('failed to close the http server:', err); + } + }); + } +} + +// Check the client status +function checkConnectionStatus() { + for (const client of clients) { + const socket = sockets.get(client); + if (socket.constructor === SocketTizen) { + try { + socket.emit('status'); + } catch (e) { + console.error(TAG, `Failed to check ${client} status`); + socket.handleEvents('disconnect'); + } + } + } +} + +function handleMessagePort(messages) { + if (messages.length === 0) { + console.error(TAG, 'Not found message'); + return; + } + + const message = messages[0]; + const event = message.key; + const value = JSON.parse(message.value); + const id = value.id; + + if (event === 'connect') { + // FIXME: The message port does not guarantee that the connection has + // been disconnected when the page is reloaded. Therefore, if a new + // connection occurs with the same id, the existing connection is + // disconnected. + if (sockets.has(id)) { + console.log(TAG, `Disconnect already connected socket: ${id}`); + const socket = sockets.get(id); + socket.handleEvents('disconnect'); + } + + const socket = new SocketTizen(id, localPort); + socket.on('connection', onConnection); + socket.connect(); + sockets.set(id, socket); + startServer(); + } else { + const socket = sockets.get(id); + socket.handleEvents(event, value.data); + } +} + +module.exports.onStart = () => { + console.log(`${TAG} onStart is called`); + if (supportMessagePort) { + console.log(TAG, 'listening tizen messageport...'); + localPort = tizen.messageport.requestLocalMessagePort('offload'); + localPort.addMessagePortListener(handleMessagePort); + + // Prevent to terminate the process + statusIntervalId = setInterval(checkConnectionStatus, 1000); + } else { + startServer(); + } +}; + +module.exports.onStop = () => { + console.log(`${TAG} onStop is called`); + if (supportMessagePort) { + for (const socket of sockets.values()) { + socket.close(); + } + if (localPort !== null) { + localPort.removeMessagePortListener(handleMessagePort); + } + if (statusIntervalId !== null) { + clearInterval(statusIntervalId); + } + } + + closeServer(); +}; + +module.exports.onRequest = () => { + console.log(`${TAG} onRequest is called`); +}; diff --git a/device_home/signaling_server/gen/socket-tizen.js b/device_home/signaling_server/gen/socket-tizen.js index bb1f726..a94ede6 100644 --- a/device_home/signaling_server/gen/socket-tizen.js +++ b/device_home/signaling_server/gen/socket-tizen.js @@ -58,7 +58,7 @@ class SocketTizen { this.handleEvents('connection', this); this.emit('connect'); } catch (error) { - console.error(TAG, `Messageport connection failed: ` + error); + console.error(TAG, 'Messageport connection failed: ' + error); } } diff --git a/device_home/signaling_server/gen/util.js b/device_home/signaling_server/gen/util.js index 38a0e8e..4c79773 100644 --- a/device_home/signaling_server/gen/util.js +++ b/device_home/signaling_server/gen/util.js @@ -4,9 +4,9 @@ function getMyAddress() { const interfaces = os.networkInterfaces(); const addresses = {}; for (const intf in interfaces) { - if (interfaces.hasOwnProperty(intf)) { + if (Object.hasOwnProperty.call(interfaces, intf)) { for (const addr in interfaces[intf]) { - if (interfaces[intf].hasOwnProperty(addr)) { + if (Object.hasOwnProperty.call(interfaces[intf], addr)) { const address = interfaces[intf][addr]; if (address.family === 'IPv4' && !address.internal) { addresses[intf] = address.address; diff --git a/packaging/config.xml.in b/packaging/config.xml.in index e2a99de..9485b1d 100644 --- a/packaging/config.xml.in +++ b/packaging/config.xml.in @@ -18,4 +18,9 @@ DeviceHomeService DeviceHomeService + + + SignalingServer + SignalingServer + diff --git a/packaging/wrtjs.spec b/packaging/wrtjs.spec index e605fba..1e27eb1 100644 --- a/packaging/wrtjs.spec +++ b/packaging/wrtjs.spec @@ -26,6 +26,7 @@ Source: %{name}-%{version}.tar.gz %define _use_nmt 0 %endif %define _use_category 0 + %define _use_d2d_offload 1 %endif BuildRequires: pkgconfig(chromium-efl) -- 2.7.4