mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-25 02:00:10 +02:00
[web] ensure that only a single socket.io transport is connected (#24422)
GitOrigin-RevId: 9397b0c85f0a889385d4761945e976ada7aa537b
This commit is contained in:
@@ -25,8 +25,6 @@ const MAX_RECONNECT_GRACEFULLY_INTERVAL_MS = getMeta(
|
||||
'ol-maxReconnectGracefullyIntervalMs'
|
||||
)
|
||||
|
||||
const BEFORE_RECONNECT = 'beforeReconnect'
|
||||
|
||||
const MAX_RETRY_CONNECT = 5
|
||||
const RETRY_WEBSOCKET = 3
|
||||
|
||||
@@ -117,7 +115,7 @@ export class ConnectionManager extends EventTarget {
|
||||
}
|
||||
|
||||
socket.on('connect', () => this.onConnect())
|
||||
socket.on('disconnect', (reason: string) => this.onDisconnect(reason))
|
||||
socket.on('disconnect', () => this.onDisconnect())
|
||||
socket.on('error', err => this.onConnectError(err))
|
||||
socket.on('connect_failed', err => this.onConnectError(err))
|
||||
socket.on('joinProjectResponse', body => this.onJoinProjectResponse(body))
|
||||
@@ -276,8 +274,7 @@ export class ConnectionManager extends EventTarget {
|
||||
this.websocketFailureCount = 0
|
||||
}
|
||||
|
||||
private onDisconnect(reason: string) {
|
||||
if (reason === BEFORE_RECONNECT) return // triggered from reconnect, ignore.
|
||||
private onDisconnect() {
|
||||
this.connectionAttempt = null
|
||||
if (this.externalHeartbeatInterval) {
|
||||
window.clearInterval(this.externalHeartbeatInterval)
|
||||
@@ -446,7 +443,7 @@ export class ConnectionManager extends EventTarget {
|
||||
if (this.socket.socket.connecting || this.socket.socket.connected) {
|
||||
// Ensure the old transport has been cleaned up.
|
||||
// Socket.disconnect() does not accept a parameter. Go one level deeper.
|
||||
this.socket.socket.onDisconnect(BEFORE_RECONNECT)
|
||||
this.socket.forceDisconnectWithoutEvent()
|
||||
}
|
||||
this.socket.socket.connect()
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ export type Socket = {
|
||||
connected: boolean
|
||||
connecting: boolean
|
||||
connect(): void
|
||||
onDisconnect(reason: string): void
|
||||
disconnect(): void
|
||||
sessionid: string
|
||||
transport?: {
|
||||
@@ -43,4 +42,5 @@ export type Socket = {
|
||||
transports: string[]
|
||||
}
|
||||
disconnect(): void
|
||||
forceDisconnectWithoutEvent(): void
|
||||
}
|
||||
|
||||
@@ -11,11 +11,12 @@ class SocketShimBase {
|
||||
constructor(socket) {
|
||||
this._socket = socket
|
||||
}
|
||||
|
||||
forceDisconnectWithoutEvent() {}
|
||||
}
|
||||
const transparentMethods = [
|
||||
'connect',
|
||||
'disconnect',
|
||||
'onDisconnect',
|
||||
'emit',
|
||||
'on',
|
||||
'removeListener',
|
||||
@@ -64,6 +65,62 @@ class SocketShimV0 extends SocketShimBase {
|
||||
constructor(socket) {
|
||||
super(socket)
|
||||
this.socket = this._socket.socket
|
||||
const self = this
|
||||
Object.defineProperty(this.socket, 'transport', {
|
||||
get() {
|
||||
return self._transport
|
||||
},
|
||||
set(v) {
|
||||
self.forceDisconnectWithoutEvent()
|
||||
self._transport = v
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
forceDisconnectWithoutEvent() {
|
||||
clearTimeout(this.socket.heartbeatTimeoutTimer)
|
||||
if (this._transport) this.forceCloseTransport(this._transport)
|
||||
}
|
||||
|
||||
forceCloseTransport(transport) {
|
||||
transport.clearTimeouts()
|
||||
if (transport instanceof io.Transport.websocket) {
|
||||
// retry closing
|
||||
transport.websocket.onopen = transport.websocket.onmessage = () =>
|
||||
transport.websocket.close()
|
||||
// mute close/error handler
|
||||
transport.websocket.onclose = transport.websocket.onerror = () => {}
|
||||
// disconnect
|
||||
try {
|
||||
transport.websocket.close()
|
||||
} catch {}
|
||||
} else if (transport instanceof io.Transport['xhr-polling']) {
|
||||
// mute data/close handler and block new polling GET requests
|
||||
transport.onData = transport.onClose = transport.get = () => {}
|
||||
// abort pending long-polling/POST request
|
||||
for (const xhr of [transport.xhr, transport.sendXHR]) {
|
||||
if (!xhr) continue // not pending
|
||||
// mute xhr callbacks
|
||||
xhr.onreadystatechange = xhr.onload = xhr.onerror = () => {}
|
||||
try {
|
||||
xhr.abort()
|
||||
} catch {}
|
||||
}
|
||||
transport.xhr = transport.sendXHR = null
|
||||
// Mark long-polling client as disconnected to avoid "ghost" connected client.
|
||||
fetch(transport.prepareUrl() + '/?disconnect=1', {
|
||||
// Let the request continue after navigating away from or reloading the page.
|
||||
keepalive: true,
|
||||
})
|
||||
// Avoid leaving a dangling response on the wire.
|
||||
.then(res => res.text())
|
||||
.catch(() => {})
|
||||
} else {
|
||||
try {
|
||||
transport.close()
|
||||
} catch {}
|
||||
debugConsole.warn('unexpected socket.io transport', transport)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user