diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..a414565ce --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "eslint.enable": false, + "prettier.enable": false, + "javascript.format.enable": false, + "editor.formatOnSave": false, + "eslint.autoFixOnSave": false, + "[javascript]": { + "editor.formatOnSave": false + }, + "[typescript]": { + "editor.formatOnSave": false + } +} diff --git a/settings.json.docker b/settings.json.docker index 79f72f05e..7fbda1aef 100644 --- a/settings.json.docker +++ b/settings.json.docker @@ -527,7 +527,7 @@ /* * Restrict socket.io transport methods */ - "socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"], + "socketTransportProtocols" : ["websocket", "polling"], "socketIo": { /* diff --git a/settings.json.template b/settings.json.template index 735b9d5e7..9c9150394 100644 --- a/settings.json.template +++ b/settings.json.template @@ -527,7 +527,7 @@ /* * Restrict socket.io transport methods */ - "socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"], + "socketTransportProtocols" : ["websocket", "polling"], "socketIo": { /* diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 55894017a..8b9d6e068 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -83,7 +83,7 @@ exports.socketio = () => { const sessioninfos = {}; exports.sessioninfos = sessioninfos; -stats.gauge('totalUsers', () => socketio ? Object.keys(socketio.sockets.sockets).length : 0); +stats.gauge('totalUsers', () => socketio ? socketio.sockets.size : 0); stats.gauge('activePads', () => { const padIds = new Set(); for (const {padId} of Object.values(sessioninfos)) { @@ -155,13 +155,13 @@ exports.handleConnect = (socket) => { * Kicks all sessions from a pad */ exports.kickSessionsFromPad = (padID) => { - if (typeof socketio.sockets.clients !== 'function') return; + if (typeof socketio.sockets.clients !== 'object') return; // skip if there is nobody on this pad if (_getRoomSockets(padID).length === 0) return; // disconnect everyone from this pad - socketio.sockets.in(padID).json.send({disconnect: 'deleted'}); + socketio.in(padID).emit('message', {disconnect: 'deleted'}); }; /** @@ -183,7 +183,7 @@ exports.handleDisconnect = async (socket) => { ` authorID:${session.author}` + (user && user.username ? ` username:${user.username}` : '')); /* eslint-enable prefer-template */ - socket.broadcast.to(session.padId).json.send({ + socket.broadcast.to(session.padId).emit('message', { type: 'COLLABROOM', data: { type: 'USER_LEAVE', @@ -216,7 +216,7 @@ exports.handleMessage = async (socket, message) => { messageLogger.warn(`Rate limited IP ${socket.request.ip}. To reduce the amount of rate ` + 'limiting that happens edit the rateLimit values in settings.json'); stats.meter('rateLimited').mark(); - socket.json.send({disconnect: 'rateLimited'}); + socket.emit('message', {disconnect: 'rateLimited'}); throw err; } } @@ -267,11 +267,11 @@ exports.handleMessage = async (socket, message) => { const {accessStatus, authorID} = await securityManager.checkAccess(auth.padID, auth.sessionID, auth.token, user); if (accessStatus !== 'grant') { - socket.json.send({accessStatus}); + socket.emit('message', {accessStatus}); throw new Error('access denied'); } if (thisSession.author != null && thisSession.author !== authorID) { - socket.json.send({disconnect: 'rejected'}); + socket.emit('message', {disconnect: 'rejected'}); throw new Error([ 'Author ID changed mid-session. Bad or missing token or sessionID?', `socket:${socket.id}`, @@ -393,10 +393,10 @@ exports.handleCustomObjectMessage = (msg, sessionID) => { if (msg.data.type === 'CUSTOM') { if (sessionID) { // a sessionID is targeted: directly to this sessionID - socketio.sockets.socket(sessionID).json.send(msg); + socketio.sockets.socket(sessionID).emit('message', msg); } else { // broadcast to all clients on this pad - socketio.sockets.in(msg.data.payload.padId).json.send(msg); + socketio.sockets.in(msg.data.payload.padId).emit('message', msg); } } }; @@ -416,7 +416,7 @@ exports.handleCustomMessage = (padID, msgString) => { time, }, }; - socketio.sockets.in(padID).json.send(msg); + socketio.sockets.in(padID).emit('message', msg); }; /** @@ -453,7 +453,7 @@ exports.sendChatMessageToPadClients = async (mt, puId, text = null, padId = null // authorManager.getAuthorName() to resolve before saving the message to the database. const promise = pad.appendChatMessage(message); message.displayName = await authorManager.getAuthorName(message.authorId); - socketio.sockets.in(padId).json.send({ + socketio.sockets.in(padId).emit('message', { type: 'COLLABROOM', data: {type: 'CHAT_MESSAGE', message}, }); @@ -483,7 +483,7 @@ const handleGetChatMessages = async (socket, {data: {start, end}}) => { }; // send the messages back to the client - socket.json.send(infoMsg); + socket.emit('message', infoMsg); }; /** @@ -500,7 +500,7 @@ const handleSuggestUserName = (socket, message) => { _getRoomSockets(padId).forEach((socket) => { const session = sessioninfos[socket.id]; if (session && session.author === unnamedId) { - socket.json.send(message); + socket.emit('message', message); } }); }; @@ -539,7 +539,7 @@ const handleUserInfoUpdate = async (socket, {data: {userInfo: {name, colorId}}}) }; // Send the other clients on the pad the update message - socket.broadcast.to(padId).json.send(infoMsg); + socket.broadcast.to(padId).emit('message',infoMsg); // Block until the authorManager has stored the new attributes. await p; @@ -654,12 +654,12 @@ const handleUserChanges = async (socket, message) => { // The client assumes that ACCEPT_COMMIT and NEW_CHANGES messages arrive in order. Make sure we // have already sent any previous ACCEPT_COMMIT and NEW_CHANGES messages. assert.equal(thisSession.rev, r); - socket.json.send({type: 'COLLABROOM', data: {type: 'ACCEPT_COMMIT', newRev}}); + socket.emit('message', {type: 'COLLABROOM', data: {type: 'ACCEPT_COMMIT', newRev}}); thisSession.rev = newRev; if (newRev !== r) thisSession.time = await pad.getRevisionDate(newRev); await exports.updatePadClients(pad); } catch (err) { - socket.json.send({disconnect: 'badChangeset'}); + socket.emit('message', {disconnect: 'badChangeset'}); stats.meter('failedChangesets').mark(); messageLogger.warn(`Failed to apply USER_CHANGES from author ${thisSession.author} ` + `(socket ${socket.id}) on pad ${thisSession.padId}: ${err.stack || err}`); @@ -716,7 +716,7 @@ exports.updatePadClients = async (pad) => { }, }; try { - socket.json.send(msg); + socket.emit('message', msg); } catch (err) { messageLogger.error(`Failed to notify user of new revision: ${err.stack || err}`); return; @@ -833,7 +833,7 @@ const handleClientReady = async (socket, message) => { // fix user's counter, works on page refresh or if user closes browser window and then rejoins sessioninfos[otherSocket.id] = {}; otherSocket.leave(sessionInfo.padId); - otherSocket.json.send({disconnect: 'userdup'}); + otherSocket.emit('message', {disconnect: 'userdup'}); } } @@ -899,7 +899,7 @@ const handleClientReady = async (socket, message) => { apool: forWire.pool, author: changesets[r].author, currentTime: changesets[r].timestamp}}; - socket.json.send(wireMsg); + socket.emit('message', wireMsg); } if (startNum === endNum) { @@ -907,7 +907,7 @@ const handleClientReady = async (socket, message) => { data: {type: 'CLIENT_RECONNECT', noChanges: true, newRev: pad.getHeadRevisionNumber()}}; - socket.json.send(Msg); + socket.emit('message', Msg); } } else { // This is a normal first connect @@ -921,7 +921,7 @@ const handleClientReady = async (socket, message) => { atext.attribs = attribsForWire.translated; } catch (e) { messageLogger.error(e.stack || e); - socket.json.send({disconnect: 'corruptPad'}); // pull the brakes + socket.emit('message', {disconnect: 'corruptPad'}); // pull the brakes throw new Error('corrupt pad'); } @@ -1005,14 +1005,14 @@ const handleClientReady = async (socket, message) => { socket.join(sessionInfo.padId); // Send the clientVars to the Client - socket.json.send({type: 'CLIENT_VARS', data: clientVars}); + socket.emit('message', {type: 'CLIENT_VARS', data: clientVars}); // Save the current revision in sessioninfos, should be the same as in clientVars sessionInfo.rev = pad.getHeadRevisionNumber(); } // Notify other users about this new user. - socket.broadcast.to(sessionInfo.padId).json.send({ + socket.broadcast.to(sessionInfo.padId).emit('message', { type: 'COLLABROOM', data: { type: 'USER_NEWINFO', @@ -1062,7 +1062,7 @@ const handleClientReady = async (socket, message) => { }, }; - socket.json.send(msg); + socket.emit('message', msg); })); await hooks.aCallAll('userJoin', { @@ -1092,7 +1092,7 @@ const handleChangesetRequest = async (socket, {data: {granularity, start, reques start = headRev; const data = await getChangesetInfo(pad, start, end, granularity); data.requestID = requestID; - socket.json.send({type: 'CHANGESET_REQ', data}); + socket.emit('message', {type: 'CHANGESET_REQ', data}); }; /** @@ -1236,13 +1236,16 @@ const composePadChangesets = async (pad, startNum, endNum) => { const _getRoomSockets = (padID) => { const ns = socketio.sockets; // Default namespace. - const adapter = ns.adapter; // We could call adapter.clients(), but that method is unnecessarily asynchronous. Replicate what // it does here, but synchronously to avoid a race condition. This code will have to change when // we update to socket.io v3. - const room = adapter.rooms[padID]; + const room = ns.adapter.rooms?.get(padID); + if (!room) return []; - return Object.keys(room.sockets).map((id) => ns.connected[id]).filter((s) => s); + + return Array.from(room) + .map(socketId => ns.sockets.get(socketId)) + .filter(socket => socket); }; /** diff --git a/src/node/hooks/express/socketio.ts b/src/node/hooks/express/socketio.ts index ba1a6a313..89034eccc 100644 --- a/src/node/hooks/express/socketio.ts +++ b/src/node/hooks/express/socketio.ts @@ -7,7 +7,7 @@ const express = require('../express'); const log4js = require('log4js'); const proxyaddr = require('proxy-addr'); const settings = require('../../utils/Settings'); -const socketio = require('socket.io'); +const {Server} = require('socket.io'); const socketIORouter = require('../../handler/SocketIORouter'); const hooks = require('../../../static/js/pluginfw/hooks'); const padMessageHandler = require('../../handler/PadMessageHandler'); @@ -48,12 +48,31 @@ exports.expressCloseServer = async () => { logger.info('All socket.io clients have disconnected'); }; +exports.socketSessionMiddleware = (socket: any, next: Function) => { + const req = socket.request; + // Express sets req.ip but socket.io does not. Replicate Express's behavior here. + if (req.ip == null) { + if (settings.trustProxy) { + req.ip = proxyaddr(req, args.app.get('trust proxy fn')); + } else { + req.ip = socket.handshake.address; + } + } + if (!req.headers.cookie) { + // socketio.js-client on node.js doesn't support cookies (see https://git.io/JU8u9), so the + // token and express_sid cookies have to be passed via a query parameter for unit tests. + req.headers.cookie = socket.handshake.query.cookie; + } + // See: https://socket.io/docs/faq/#Usage-with-express-session + express.sessionMiddleware(req, {}, next); +}; + exports.expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Function) => { // init socket.io and redirect all requests to the MessageHandler // there shouldn't be a browser that isn't compatible to all // transports in this list at once // e.g. XHR is disabled in IE by default, so in IE it should use jsonp-polling - io = socketio({ + io = new Server(args.server, { transports: settings.socketTransportProtocols, }).listen(args.server, { /* @@ -79,33 +98,20 @@ exports.expressCreateServer = (hookName:string, args:ArgsExpressType, cb:Functio maxHttpBufferSize: settings.socketIo.maxHttpBufferSize, }); - io.on('connect', (socket:any) => { + io.on('connection', (socket:any) => { sockets.add(socket); socketsEvents.emit('updated'); + // https://socket.io/docs/v3/faq/index.html + const session = socket.request.session; + session.connections++; + session.save(); socket.on('disconnect', () => { sockets.delete(socket); socketsEvents.emit('updated'); }); }); - io.use((socket:any, next: Function) => { - const req = socket.request; - // Express sets req.ip but socket.io does not. Replicate Express's behavior here. - if (req.ip == null) { - if (settings.trustProxy) { - req.ip = proxyaddr(req, args.app.get('trust proxy fn')); - } else { - req.ip = socket.handshake.address; - } - } - if (!req.headers.cookie) { - // socketio.js-client on node.js doesn't support cookies (see https://git.io/JU8u9), so the - // token and express_sid cookies have to be passed via a query parameter for unit tests. - req.headers.cookie = socket.handshake.query.cookie; - } - // See: https://socket.io/docs/faq/#Usage-with-express-session - express.sessionMiddleware(req, {}, next); - }); + io.use(exports.socketSessionMiddleware); io.use((socket:any, next:Function) => { socket.conn.on('packet', (packet:string) => { diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 7125607ea..48955f291 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -126,7 +126,7 @@ exports.ssl = false; /** * socket.io transport methods **/ -exports.socketTransportProtocols = ['xhr-polling', 'jsonp-polling', 'htmlfile']; +exports.socketTransportProtocols = ['websocket', 'polling']; exports.socketIo = { /** diff --git a/src/package.json b/src/package.json index ff8915b76..bf98e0929 100644 --- a/src/package.json +++ b/src/package.json @@ -62,7 +62,8 @@ "resolve": "1.22.8", "security": "1.0.0", "semver": "^7.6.0", - "socket.io": "^2.5.0", + "socket.io": "^3.1.2", + "socket.io-client": "^3.1.2", "superagent": "^8.1.2", "terser": "^5.27.1", "threads": "^1.7.0", @@ -78,11 +79,11 @@ "etherpad-lite": "node/server.ts" }, "devDependencies": { - "cypress": "^13.6.4", "@types/async": "^3.2.24", "@types/express": "^4.17.21", "@types/node": "^20.11.19", "@types/underscore": "^1.11.15", + "cypress": "^13.6.4", "eslint": "^8.56.0", "eslint-config-etherpad": "^3.0.22", "etherpad-cli-client": "^2.0.2", @@ -108,8 +109,8 @@ }, "scripts": { "lint": "eslint .", - "test": "mocha --import=tsx --timeout 120000 --recursive tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs", - "test-container": "mocha --import=tsx --timeout 5000 tests/container/specs/api", + "test": "mocha --import=tsx --timeout 990000 --recursive tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs", + "test-container": "mocha --import=tsx --timeout 99000 tests/container/specs/api", "dev": "node --import tsx node/server.ts", "prod": "node --import tsx node/server.ts", "ts-check": "tsc --noEmit", diff --git a/src/pnpm-lock.yaml b/src/pnpm-lock.yaml index e8b14dffe..9bb734a5c 100644 --- a/src/pnpm-lock.yaml +++ b/src/pnpm-lock.yaml @@ -102,8 +102,11 @@ dependencies: specifier: ^7.6.0 version: 7.6.0 socket.io: - specifier: ^2.5.0 - version: 2.5.0 + specifier: ^3.1.2 + version: 3.1.2 + socket.io-client: + specifier: ^3.1.2 + version: 3.1.2 superagent: specifier: ^8.1.2 version: 8.1.2 @@ -633,12 +636,26 @@ packages: '@types/node': 20.11.19 dev: true + /@types/component-emitter@1.2.14: + resolution: {integrity: sha512-lmPil1g82wwWg/qHSxMWkSKyJGQOK+ejXeMAAWyxNtVUD0/Ycj2maL63RAqpxVfdtvTfZkRnqzB0A9ft59y69g==} + dev: false + /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: '@types/node': 20.11.19 dev: true + /@types/cookie@0.4.1: + resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + dev: false + + /@types/cors@2.8.17: + resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} + dependencies: + '@types/node': 20.11.17 + dev: false + /@types/debug@4.1.12: resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} dependencies: @@ -947,6 +964,7 @@ packages: /after@0.8.2: resolution: {integrity: sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==} + dev: true /agent-base@7.1.0: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} @@ -1123,6 +1141,7 @@ packages: /arraybuffer.slice@0.0.7: resolution: {integrity: sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==} + dev: true /asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} @@ -1225,6 +1244,7 @@ packages: /blob@0.0.5: resolution: {integrity: sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==} + dev: true /bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} @@ -1462,16 +1482,14 @@ packages: /component-bind@1.0.0: resolution: {integrity: sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==} - - /component-emitter@1.2.1: - resolution: {integrity: sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==} - dev: false + dev: true /component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} /component-inherit@0.0.3: resolution: {integrity: sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==} + dev: true /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1526,6 +1544,14 @@ packages: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: true + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -1635,6 +1661,7 @@ packages: optional: true dependencies: ms: 2.0.0 + dev: true /debug@3.2.7(supports-color@8.1.1): resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} @@ -1648,18 +1675,6 @@ packages: supports-color: 8.1.1 dev: true - /debug@4.1.1: - resolution: {integrity: sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==} - deprecated: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797) - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - dev: false - /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -1821,6 +1836,26 @@ packages: - bufferutil - supports-color - utf-8-validate + dev: true + + /engine.io-client@4.1.4: + resolution: {integrity: sha512-843fqAdKeUMFqKi1sSjnR11tJ4wi8sIefu6+JC1OzkkJBmjtc/gM/rZ53tJfu5Iae/3gApm5veoS+v+gtT0+Fg==} + dependencies: + base64-arraybuffer: 0.1.4 + component-emitter: 1.3.1 + debug: 4.3.4(supports-color@8.1.1) + engine.io-parser: 4.0.3 + has-cors: 1.1.0 + parseqs: 0.0.6 + parseuri: 0.0.6 + ws: 7.4.6 + xmlhttprequest-ssl: 1.6.3 + yeast: 0.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false /engine.io-parser@2.2.1: resolution: {integrity: sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==} @@ -1830,16 +1865,25 @@ packages: base64-arraybuffer: 0.1.4 blob: 0.0.5 has-binary2: 1.0.3 + dev: true - /engine.io@3.6.1: - resolution: {integrity: sha512-dfs8EVg/i7QjFsXxn7cCRQ+Wai1G1TlEvHhdYEi80fxn5R1vZ2K661O6v/rezj1FP234SZ14r9CmJke99iYDGg==} + /engine.io-parser@4.0.3: + resolution: {integrity: sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==} engines: {node: '>=8.0.0'} + dependencies: + base64-arraybuffer: 0.1.4 + dev: false + + /engine.io@4.1.2: + resolution: {integrity: sha512-t5z6zjXuVLhXDMiFJPYsPOWEER8B0tIsD3ETgw19S1yg9zryvUfY3Vhtk3Gf4sihw/bQGIqQ//gjvVlu+Ca0bQ==} + engines: {node: '>=10.0.0'} dependencies: accepts: 1.3.8 base64id: 2.0.0 cookie: 0.4.2 - debug: 4.1.1 - engine.io-parser: 2.2.1 + cors: 2.8.5 + debug: 4.3.4(supports-color@8.1.1) + engine.io-parser: 4.0.3 ws: 7.4.6 transitivePeerDependencies: - bufferutil @@ -2844,6 +2888,7 @@ packages: resolution: {integrity: sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==} dependencies: isarray: 2.0.1 + dev: true /has-cors@1.1.0: resolution: {integrity: sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==} @@ -3096,6 +3141,7 @@ packages: /indexof@0.0.1: resolution: {integrity: sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==} + dev: true /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} @@ -3317,6 +3363,7 @@ packages: /isarray@2.0.1: resolution: {integrity: sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==} + dev: true /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -3877,6 +3924,11 @@ packages: resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} dev: false + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: false + /object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} @@ -4562,8 +4614,8 @@ packages: is-fullwidth-code-point: 3.0.0 dev: true - /socket.io-adapter@1.1.2: - resolution: {integrity: sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==} + /socket.io-adapter@2.1.0: + resolution: {integrity: sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==} dev: false /socket.io-client@2.5.0: @@ -4584,6 +4636,24 @@ packages: - bufferutil - supports-color - utf-8-validate + dev: true + + /socket.io-client@3.1.2: + resolution: {integrity: sha512-fXhF8plHrd7U14A7K0JPOmZzpmGkLpIS6623DzrBZqYzI/yvlP4fA3LnxwthEVgiHmn2uJ4KjdnQD8A03PuBWQ==} + engines: {node: '>=10.0.0'} + dependencies: + '@types/component-emitter': 1.2.14 + backo2: 1.0.2 + component-emitter: 1.3.1 + debug: 4.3.4(supports-color@8.1.1) + engine.io-client: 4.1.4 + parseuri: 0.0.6 + socket.io-parser: 4.0.5 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false /socket.io-parser@3.3.3: resolution: {integrity: sha512-qOg87q1PMWWTeO01768Yh9ogn7chB9zkKtQnya41Y355S0UmpXgpcrFwAgjYJxu9BdKug5r5e9YtVSeWhKBUZg==} @@ -4593,27 +4663,32 @@ packages: isarray: 2.0.1 transitivePeerDependencies: - supports-color + dev: true - /socket.io-parser@3.4.3: - resolution: {integrity: sha512-1rE4dZN3kCI/E5wixd393hmbqa78vVpkKmnEJhLeWoS/C5hbFYAbcSfnWoaVH43u9ToUVtzKjguxEZq+1XZfCQ==} + /socket.io-parser@4.0.5: + resolution: {integrity: sha512-sNjbT9dX63nqUFIOv95tTVm6elyIU4RvB1m8dOeZt+IgWwcWklFDOdmGcfo3zSiRsnR/3pJkjY5lfoGqEe4Eig==} engines: {node: '>=10.0.0'} dependencies: - component-emitter: 1.2.1 - debug: 4.1.1 - isarray: 2.0.1 + '@types/component-emitter': 1.2.14 + component-emitter: 1.3.1 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: false - /socket.io@2.5.0: - resolution: {integrity: sha512-gGunfS0od3VpwDBpGwVkzSZx6Aqo9uOcf1afJj2cKnKFAoyl16fvhpsUhmUFd4Ldbvl5JvRQed6eQw6oQp6n8w==} + /socket.io@3.1.2: + resolution: {integrity: sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw==} + engines: {node: '>=10.0.0'} dependencies: - debug: 4.1.1 - engine.io: 3.6.1 - has-binary2: 1.0.3 - socket.io-adapter: 1.1.2 - socket.io-client: 2.5.0 - socket.io-parser: 3.4.3 + '@types/cookie': 0.4.1 + '@types/cors': 2.8.17 + '@types/node': 20.11.17 + accepts: 1.3.8 + base64id: 2.0.0 + debug: 4.3.4(supports-color@8.1.1) + engine.io: 4.1.2 + socket.io-adapter: 2.1.0 + socket.io-parser: 4.0.5 transitivePeerDependencies: - bufferutil - supports-color @@ -4891,6 +4966,7 @@ packages: /to-array@0.1.4: resolution: {integrity: sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A==} + dev: true /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} diff --git a/src/static/js/collab_client.js b/src/static/js/collab_client.js index 11a3a3dc6..69c8e41f3 100644 --- a/src/static/js/collab_client.js +++ b/src/static/js/collab_client.js @@ -156,7 +156,7 @@ const getCollabClient = (ace2editor, serverVars, initialUserInfo, options, _pad) }; const sendMessage = (msg) => { - getSocket().json.send( + getSocket().emit('message', { type: 'COLLABROOM', component: 'pad', diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 802f1d476..df409372a 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -207,7 +207,7 @@ const sendClientReady = (isReconnect) => { msg.reconnect = true; } - socket.json.send(msg); + socket.emit("message", msg); }; const handshake = async () => { diff --git a/src/static/js/timeslider.js b/src/static/js/timeslider.js index 8c5993145..4cc5d45a6 100644 --- a/src/static/js/timeslider.js +++ b/src/static/js/timeslider.js @@ -95,7 +95,7 @@ const init = () => { // sends a message over the socket const sendSocketMsg = (type, data) => { - socket.json.send({ + socket.emit("message", { component: 'pad', // FIXME: Remove this stupidity! type, data, diff --git a/src/templates/pad.html b/src/templates/pad.html index 54e1897d9..69671ca17 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -442,7 +442,7 @@ <% e.begin_block("scripts"); %> - + diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html index e26cd11e7..ee45f4575 100644 --- a/src/templates/timeslider.html +++ b/src/templates/timeslider.html @@ -250,7 +250,7 @@ - + diff --git a/src/tests/backend/common.js b/src/tests/backend/common.js index b331cf05d..05274edc5 100644 --- a/src/tests/backend/common.js +++ b/src/tests/backend/common.js @@ -147,7 +147,6 @@ exports.connect = async (res = null) => { } const socket = io(`${exports.baseUrl}/`, { forceNew: true, // Different tests will have different query parameters. - path: '/socket.io', // socketio.js-client on node.js doesn't support cookies (see https://git.io/JU8u9), so the // express_sid cookie must be passed as a query parameter. query: {cookie: reqCookieHdr, padId}, @@ -172,7 +171,7 @@ exports.connect = async (res = null) => { */ exports.handshake = async (socket, padId, token = padutils.generateAuthorToken()) => { logger.debug('sending CLIENT_READY...'); - socket.send({ + socket.emit('message', { component: 'pad', type: 'CLIENT_READY', padId, @@ -189,7 +188,8 @@ exports.handshake = async (socket, padId, token = padutils.generateAuthorToken() * Convenience wrapper around `socket.send()` that waits for acknowledgement. */ exports.sendMessage = async (socket, message) => await new Promise((resolve, reject) => { - socket.send(message, (errInfo) => { + if(message.type === "CHANGESET_REQ") + socket.emit('message', message, (errInfo) => { if (errInfo != null) { const {name, message} = errInfo; const err = new Error(message); diff --git a/src/tests/backend/specs/chat.js b/src/tests/backend/specs/chat.js index aefa64183..88fda9d07 100644 --- a/src/tests/backend/specs/chat.js +++ b/src/tests/backend/specs/chat.js @@ -32,7 +32,7 @@ const checkHook = async (hookName, checkFn) => { }; const sendMessage = (socket, data) => { - socket.send({ + socket.emit('message', { type: 'COLLABROOM', component: 'pad', data, diff --git a/src/tests/backend/specs/socketio.js b/src/tests/backend/specs/socketio.js index 15f561774..8a65213f8 100644 --- a/src/tests/backend/specs/socketio.js +++ b/src/tests/backend/specs/socketio.js @@ -390,7 +390,7 @@ describe(__filename, function () { handleMessage(socket, message) { assert.fail('wrong handler called'); } }()); socket = await common.connect(); - socket.send(want); + socket.emit('message', want); assert.deepEqual(await got, want); }); @@ -398,7 +398,7 @@ describe(__filename, function () { const AckErr = class extends Error { constructor(name, ...args) { super(...args); this.name = name; } }; - socket.send(message, + socket.emit('message', message, (errj, val) => errj != null ? reject(new AckErr(errj.name, errj.message)) : resolve(val)); });