From 09fe41af7572644e7019ab925adbb8019f53e694 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Mon, 12 Sep 2022 12:04:36 +0300 Subject: [PATCH] [feature] Add socket.io module --- Common/config/default.json | 9 ++ Common/sources/tenantManager.js | 2 +- Common/sources/utils.js | 1 + DocService/npm-shrinkwrap.json | 138 +++++++++++++++++++++++++++++ DocService/package.json | 1 + DocService/sources/DocsCoServer.js | 67 +++++++++----- 6 files changed, 196 insertions(+), 22 deletions(-) diff --git a/Common/config/default.json b/Common/config/default.json index d3600d29d..5a4abca0a 100644 --- a/Common/config/default.json +++ b/Common/config/default.json @@ -268,6 +268,15 @@ "disable_cors": true, "websocket": true }, + "socketio": { + "connection": { + "path": "/doc/socket.io/", + "serveClient": false, + "pingTimeout": 20000, + "pingInterval": 25000, + "maxHttpBufferSize": 1e8 + } + }, "callbackBackoffOptions": { "retries": 0, "timeout":{ diff --git a/Common/sources/tenantManager.js b/Common/sources/tenantManager.js index 66dbb9679..2317cbc83 100644 --- a/Common/sources/tenantManager.js +++ b/Common/sources/tenantManager.js @@ -77,7 +77,7 @@ function getTenant(ctx, domain) { return tenant; } function getTenantByConnection(ctx, conn) { - return isMultitenantMode() ? getTenant(ctx, utils.getDomainByConnection(ctx, conn)) : getDefautTenant(); + return isMultitenantMode() ? getTenant(ctx, utils.getDomainByConnection(ctx, conn.request)) : getDefautTenant(); } function getTenantByRequest(ctx, req) { return isMultitenantMode() ? getTenant(ctx, utils.getDomainByRequest(ctx, req)) : getDefautTenant(); diff --git a/Common/sources/utils.js b/Common/sources/utils.js index 4cc853b9d..8c34b9fde 100644 --- a/Common/sources/utils.js +++ b/Common/sources/utils.js @@ -684,6 +684,7 @@ function getBaseUrl(protocol, hostHeader, forwardedProtoHeader, forwardedHostHea return url; } function getBaseUrlByConnection(conn) { + conn = conn.request; return getBaseUrl('', conn.headers['host'], conn.headers['x-forwarded-proto'], conn.headers['x-forwarded-host'], conn.headers['x-forwarded-prefix']); } function getBaseUrlByRequest(req) { diff --git a/DocService/npm-shrinkwrap.json b/DocService/npm-shrinkwrap.json index 08430ea86..4487f96fc 100644 --- a/DocService/npm-shrinkwrap.json +++ b/DocService/npm-shrinkwrap.json @@ -4,6 +4,26 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, + "@types/node": { + "version": "18.7.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.16.tgz", + "integrity": "sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg==" + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -59,6 +79,11 @@ "resolved": "https://registry.npmjs.org/base64-stream/-/base64-stream-1.0.0.tgz", "integrity": "sha512-BQQZftaO48FcE1Kof9CmXMFaAdqkcNorgc8CxesZv9nMbbTF1EFyQe89UOuh//QMmdtfUDXyO8rgUalemL5ODA==" }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, "body-parser": { "version": "1.18.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", @@ -187,6 +212,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cron": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/cron/-/cron-1.5.0.tgz", @@ -290,6 +324,48 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "engine.io": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" + }, + "dependencies": { + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1115,6 +1191,63 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, + "socket.io": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.2.tgz", + "integrity": "sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==", + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.0", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.2.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==" + }, + "socket.io-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "sockjs": { "version": "0.3.21", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", @@ -1256,6 +1389,11 @@ "resolved": "https://registry.npmjs.org/windows-locale/-/windows-locale-1.0.1.tgz", "integrity": "sha512-X8B22Cg9njwV4h3C5j28xmZ2eWaO69j63WhReeglB69LOT3LoqSO4Vb6TTVSfFikh4KQ9qBOJb6+WvR4tVLTfQ==" }, + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/DocService/package.json b/DocService/package.json index f3694edd7..c2fb20a89 100644 --- a/DocService/package.json +++ b/DocService/package.json @@ -33,6 +33,7 @@ "pg": "^8.5.1", "redis": "^2.8.0", "retry": "^0.12.0", + "socket.io": "^4.5.2", "sockjs": "^0.3.21", "underscore": "^1.13.1", "utf7": "^1.0.2", diff --git a/DocService/sources/DocsCoServer.js b/DocService/sources/DocsCoServer.js index 84919392f..9e1175c45 100644 --- a/DocService/sources/DocsCoServer.js +++ b/DocService/sources/DocsCoServer.js @@ -73,6 +73,7 @@ 'use strict'; const sockjs = require('sockjs'); +const { Server } = require("socket.io"); const _ = require('underscore'); const url = require('url'); const os = require('os'); @@ -150,6 +151,7 @@ const cfgErrorFiles = configCommon.get('FileConverter.converter.errorfiles'); const cfgOpenProtectedFile = config.get('server.openProtectedFile'); const cfgRefreshLockInterval = ms(configCommon.get('wopi.refreshLockInterval')); const cfgTokenRequiredParams = config.get('server.tokenRequiredParams'); +const cfgSocketIoConnection = configCommon.get('services.CoAuthoring.socketio.connection'); const EditorTypes = { document : 0, @@ -1308,9 +1310,31 @@ function encryptPasswordParams(ctx, data) { } exports.encryptPasswordParams = encryptPasswordParams; exports.install = function(server, callbackFunction) { - var sockjs_echo = sockjs.createServer(cfgSockjs); + const io = new Server(server, cfgSocketIoConnection); - sockjs_echo.on('connection', function(conn) { + io.use((socket, next) => { + co(function*(){ + let ctx = new operationContext.Context(); + let checkJwtRes; + try { + ctx.initFromConnection(socket); + ctx.logger.info('io.use start'); + let handshake = socket.handshake; + if (cfgTokenEnableBrowser) { + checkJwtRes = yield checkJwt(ctx, handshake?.auth?.token, commonDefines.c_oAscSecretType.Browser); + } + } catch (err) { + ctx.logger.info('io.use error: %s', err.stack); + } finally { + ctx.logger.info('io.use end'); + next(checkJwtRes.decoded ? undefined : new Error("not authorized")); + } + + + }); + }); + + io.on('connection', function(conn) { if (!conn) { operationContext.global.logger.error("null == conn"); return; @@ -1325,7 +1349,7 @@ exports.install = function(server, callbackFunction) { conn.sessionIsSendWarning = false; conn.sessionTimeConnect = conn.sessionTimeLastAction = new Date().getTime(); - conn.on('data', function(message) { + conn.on('message', function(data) { return co(function* () { var docId = 'null'; let ctx = new operationContext.Context(); @@ -1336,7 +1360,6 @@ exports.install = function(server, callbackFunction) { startDate = new Date(); } - var data = JSON.parse(message); docId = conn.docId; ctx.logger.info('data.type = %s', data.type); if(getIsShutdown()) @@ -1392,7 +1415,7 @@ exports.install = function(server, callbackFunction) { yield* checkEndAuthLock(ctx, data.unlock, data.isSave, docId, conn.user.id, data.releaseLocks, data.deleteIndex, conn); break; case 'close': - yield* closeDocument(ctx, conn, false); + yield* closeDocument(ctx, conn); break; case 'versionHistory' : { let cmd = new commonDefines.InputCommand(data.cmd); @@ -1433,7 +1456,7 @@ exports.install = function(server, callbackFunction) { delete conn.authChangesAck; break; default: - ctx.logger.debug("unknown command %s", message); + ctx.logger.debug("unknown command %d", data); break; } if(clientStatsD) { @@ -1446,17 +1469,12 @@ exports.install = function(server, callbackFunction) { } }); }); - conn.on('error', function() { - let ctx = new operationContext.Context(); - ctx.initFromConnection(conn); - ctx.logger.error("On error"); - }); - conn.on('close', function() { + conn.on("disconnect", function(reason) { return co(function* () { let ctx = new operationContext.Context(); try { ctx.initFromConnection(conn); - yield* closeDocument(ctx, conn, true); + yield* closeDocument(ctx, conn, reason); } catch (err) { ctx.logger.error('Error conn close: %s', err.stack); } @@ -1465,12 +1483,19 @@ exports.install = function(server, callbackFunction) { _checkLicense(ctx, conn); }); + io.engine.on("connection_error", (err) => { + console.log(err.req); // the request object + console.log(err.code); // the error code, for example 1 + console.log(err.message); // the error message, for example "Session ID unknown" + console.log(err.context); // some additional error context + }); /** * + * @param ctx * @param conn - * @param isCloseConnection - закрываем ли мы окончательно соединение + * @param reason - the reason of the disconnection (either client or server-side) */ - function* closeDocument(ctx, conn, isCloseConnection) { + function* closeDocument(ctx, conn, reason) { var userLocks, reconnected = false, bHasEditors, bHasChanges; var docId = conn.docId; if (null == docId) { @@ -1480,9 +1505,9 @@ exports.install = function(server, callbackFunction) { let participantsTimestamp; var tmpUser = conn.user; var isView = tmpUser.view; - ctx.logger.info("Connection closed or timed out: isCloseConnection = %s", isCloseConnection); + ctx.logger.info("Connection closed or timed out: reason = %s", reason); var isCloseCoAuthoringTmp = conn.isCloseCoAuthoring; - if (isCloseConnection) { + if (reason) { //Notify that participant has gone connections = _.reject(connections, function(el) { return el.id === conn.id;//Delete this connection @@ -3145,10 +3170,10 @@ exports.install = function(server, callbackFunction) { return licenseType; } - sockjs_echo.installHandlers(server, {prefix: '/doc/['+constants.DOC_ID_PATTERN+']*/c', log: function(severity, message) { - //TODO: handle severity - operationContext.global.logger.info(message); - }}); + // sockjs_echo.installHandlers(server, {prefix: '/doc/['+constants.DOC_ID_PATTERN+']*/c', log: function(severity, message) { + // //TODO: handle severity + // operationContext.global.logger.info(message); + // }}); //publish subscribe message brocker function pubsubOnMessage(msg) {