mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #2677 from overleaf/jpa-socket-io-v2-shim
[socket.io] add support for v2 using a compatibility shim GitOrigin-RevId: ef52a3d8e4fdf191f6a8c1a530dfce25f3a11466
This commit is contained in:
parent
5c6ddfd53a
commit
a0359009f9
2 changed files with 227 additions and 3 deletions
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
/* global io */
|
/* global io */
|
||||||
|
|
||||||
define([], function() {
|
define(['./SocketIoShim'], function(SocketIoShim) {
|
||||||
let ConnectionManager
|
let ConnectionManager
|
||||||
const ONEHOUR = 1000 * 60 * 60
|
const ONEHOUR = 1000 * 60 * 60
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ define([], function() {
|
||||||
console.error(
|
console.error(
|
||||||
'Socket.io javascript not loaded. Please check that the real-time service is running and accessible.'
|
'Socket.io javascript not loaded. Please check that the real-time service is running and accessible.'
|
||||||
)
|
)
|
||||||
this.ide.socket = { on() {} }
|
this.ide.socket = SocketIoShim.stub()
|
||||||
this.$scope.$apply(() => {
|
this.$scope.$apply(() => {
|
||||||
return (this.$scope.state.error =
|
return (this.$scope.state.error =
|
||||||
'Could not connect to websocket server :(')
|
'Could not connect to websocket server :(')
|
||||||
|
@ -116,7 +116,7 @@ define([], function() {
|
||||||
pathname: '/socket.io'
|
pathname: '/socket.io'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.ide.socket = io.connect(
|
this.ide.socket = SocketIoShim.connect(
|
||||||
parsedURL.origin,
|
parsedURL.origin,
|
||||||
{
|
{
|
||||||
resource: parsedURL.pathname.slice(1),
|
resource: parsedURL.pathname.slice(1),
|
||||||
|
|
224
services/web/frontend/js/ide/connection/SocketIoShim.js
Normal file
224
services/web/frontend/js/ide/connection/SocketIoShim.js
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
/* global io */
|
||||||
|
|
||||||
|
define([], function() {
|
||||||
|
class SocketShimBase {
|
||||||
|
static connect(url, options) {
|
||||||
|
return new SocketShimBase()
|
||||||
|
}
|
||||||
|
constructor(socket) {
|
||||||
|
this._socket = socket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const transparentMethods = [
|
||||||
|
'connect',
|
||||||
|
'disconnect',
|
||||||
|
'emit',
|
||||||
|
'on',
|
||||||
|
'removeListener'
|
||||||
|
]
|
||||||
|
for (let method of transparentMethods) {
|
||||||
|
SocketShimBase.prototype[method] = function() {
|
||||||
|
this._socket[method].apply(this._socket, arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SocketShimNoop extends SocketShimBase {
|
||||||
|
static connect() {
|
||||||
|
return new SocketShimNoop()
|
||||||
|
}
|
||||||
|
constructor(socket) {
|
||||||
|
super(socket)
|
||||||
|
this.socket = {
|
||||||
|
get connected() {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
get sessionid() {
|
||||||
|
return undefined
|
||||||
|
},
|
||||||
|
get transport() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
|
||||||
|
connect() {},
|
||||||
|
disconnect(reason) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connect() {}
|
||||||
|
disconnect(reason) {}
|
||||||
|
emit() {}
|
||||||
|
on() {}
|
||||||
|
removeListener() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SocketShimV0 extends SocketShimBase {
|
||||||
|
static connect(url, options) {
|
||||||
|
return new SocketShimV0(
|
||||||
|
io.connect(
|
||||||
|
url,
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
constructor(socket) {
|
||||||
|
super(socket)
|
||||||
|
this.socket = this._socket.socket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SocketShimV2 extends SocketShimBase {
|
||||||
|
static connect(url, options) {
|
||||||
|
options.forceNew = options['force new connection']
|
||||||
|
// .resource has no leading slash, path wants to see one.
|
||||||
|
options.path = '/' + options.resource
|
||||||
|
options.reconnection = options.reconnect
|
||||||
|
options.timeout = options['connect timeout']
|
||||||
|
return new SocketShimV2(url, options)
|
||||||
|
}
|
||||||
|
static get EVENT_MAP() {
|
||||||
|
// Use the v2 event names transparently to the frontend.
|
||||||
|
const connectionFailureEvents = [
|
||||||
|
'connect_error',
|
||||||
|
'connect_timeout',
|
||||||
|
'error'
|
||||||
|
]
|
||||||
|
return new Map([
|
||||||
|
['connect_failed', connectionFailureEvents],
|
||||||
|
['error', connectionFailureEvents]
|
||||||
|
])
|
||||||
|
}
|
||||||
|
_on(event, handler) {
|
||||||
|
// Keep track of our event listeners.
|
||||||
|
// We move them to a new socket in ._replaceSocketWithNewInstance()
|
||||||
|
if (!this._events.has(event)) {
|
||||||
|
this._events.set(event, [handler])
|
||||||
|
} else {
|
||||||
|
this._events.get(event).push(handler)
|
||||||
|
}
|
||||||
|
this._socket.on(event, handler)
|
||||||
|
}
|
||||||
|
on(event, handler) {
|
||||||
|
if (SocketShimV2.EVENT_MAP.has(event)) {
|
||||||
|
for (const v2Event of SocketShimV2.EVENT_MAP.get(event)) {
|
||||||
|
this._on(v2Event, handler)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._on(event, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_removeListener(event, handler) {
|
||||||
|
// Keep track of our event listeners.
|
||||||
|
// We move them to a new socket in ._replaceSocketWithNewInstance()
|
||||||
|
if (this._events.has(event)) {
|
||||||
|
const listeners = this._events.get(event)
|
||||||
|
const pos = listeners.indexOf(handler)
|
||||||
|
if (pos !== -1) {
|
||||||
|
listeners.splice(pos, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._socket.removeListener(event, handler)
|
||||||
|
}
|
||||||
|
removeListener(event, handler) {
|
||||||
|
if (SocketShimV2.EVENT_MAP.has(event)) {
|
||||||
|
for (const v2Event of SocketShimV2.EVENT_MAP.get(event)) {
|
||||||
|
this._removeListener(v2Event, handler)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._removeListener(event, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static createNewSocket(url, options) {
|
||||||
|
// open a brand new connection for the default namespace '/'
|
||||||
|
// The old socket can still leak 'disconnect' events from the teardown
|
||||||
|
// of the old transport. The leaking 'disconnect' events interfere with
|
||||||
|
// the _new_ connection and cancel the new connect attempt.
|
||||||
|
// Also skip the caching in these locations:
|
||||||
|
// - `io.connect()` caches `io.Manager`s in `io.managers`
|
||||||
|
// - `io.Manager().socket()` caches `io.Socket`s in its `this.nsps`
|
||||||
|
return io.Manager(url, options).socket('/', options)
|
||||||
|
}
|
||||||
|
|
||||||
|
_replaceSocketWithNewInstance() {
|
||||||
|
const oldSocket = this._socket
|
||||||
|
const newSocket = SocketShimV2.createNewSocket(this._url, this._options)
|
||||||
|
|
||||||
|
// move our existing event handlers to the new socket
|
||||||
|
this._events.forEach((listeners, event) => {
|
||||||
|
for (const listener of listeners) {
|
||||||
|
oldSocket.removeListener(event, listener)
|
||||||
|
newSocket.on(event, listener)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (oldSocket.connected) {
|
||||||
|
// We overwrite the reference to oldSocket soon.
|
||||||
|
// Make sure we are disconnected.
|
||||||
|
oldSocket.disconnect()
|
||||||
|
}
|
||||||
|
this._socket = newSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
// have the same logic behind socket.connect and socket.socket.connect
|
||||||
|
this._replaceSocketWithNewInstance()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(url, options) {
|
||||||
|
super(SocketShimV2.createNewSocket(url, options))
|
||||||
|
this._url = url
|
||||||
|
this._options = options
|
||||||
|
this._events = new Map()
|
||||||
|
|
||||||
|
const self = this
|
||||||
|
function _getEngine() {
|
||||||
|
return (self._socket.io && self._socket.io.engine) || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket = {
|
||||||
|
get connected() {
|
||||||
|
return self._socket.connected
|
||||||
|
},
|
||||||
|
get sessionid() {
|
||||||
|
if (self._socket.id) {
|
||||||
|
return self._socket.id
|
||||||
|
}
|
||||||
|
// socket.id is discarded upon disconnect
|
||||||
|
// the id is still available in the internal state
|
||||||
|
return _getEngine().id
|
||||||
|
},
|
||||||
|
get transport() {
|
||||||
|
return _getEngine().transport
|
||||||
|
},
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
self._replaceSocketWithNewInstance()
|
||||||
|
},
|
||||||
|
disconnect(reason) {
|
||||||
|
return self._socket.disconnect(reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let current
|
||||||
|
if (typeof io === 'undefined' || !io) {
|
||||||
|
sl_console.log('[socket.io] Shim: socket.io is not loaded, returning noop')
|
||||||
|
current = SocketShimNoop
|
||||||
|
} else if (typeof io.version === 'string' && io.version.slice(0, 1) === '0') {
|
||||||
|
sl_console.log('[socket.io] Shim: detected v0')
|
||||||
|
current = SocketShimV0
|
||||||
|
} else {
|
||||||
|
// socket.io v2 does not have a global io.version attribute.
|
||||||
|
sl_console.log('[socket.io] Shim: detected v2')
|
||||||
|
current = SocketShimV2
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
SocketShimNoop,
|
||||||
|
SocketShimV0,
|
||||||
|
SocketShimV2,
|
||||||
|
current,
|
||||||
|
connect: current.connect,
|
||||||
|
stub: () => new SocketShimNoop()
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in a new issue