mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-12 21:23:45 +01:00
[web] editor connection fixes (#23807)
* [haproxy] add support for xhr-polling in real-time * [web] try 3 WebSockets then fallback to xhr-polling when (re-)connecting * [web] ensure old socket.io transport has been closed when reconnecting * [web] close xhr-polling transport before leaving the page This will de-register the client, leave the project and clean up the connected clients tracking ASAP. * [web] rename constant to avoid confusion Co-authored-by: Alf Eaton <alf.eaton@overleaf.com> --------- Co-authored-by: Alf Eaton <alf.eaton@overleaf.com> GitOrigin-RevId: 55e500c341145947e5589875438fc796394b6ae4
This commit is contained in:
committed by
Copybot
co-authored by
Alf Eaton
parent
82f2e47476
commit
4d857b7937
@@ -22,7 +22,10 @@ const JOIN_PROJECT_RATE_LIMITED_DELAY = 15 * 1000
|
||||
const RECONNECT_GRACEFULLY_RETRY_INTERVAL_MS = 5000
|
||||
const MAX_RECONNECT_GRACEFULLY_INTERVAL_MS = 45 * 1000
|
||||
|
||||
const BEFORE_RECONNECT = 'beforeReconnect'
|
||||
|
||||
const MAX_RETRY_CONNECT = 5
|
||||
const RETRY_WEBSOCKET = 3
|
||||
|
||||
const externalSocketHeartbeat = isSplitTestEnabled('external-socket-heartbeat')
|
||||
|
||||
@@ -49,6 +52,7 @@ export class ConnectionManager extends EventTarget {
|
||||
private protocolVersion = -1
|
||||
private readonly idleDisconnectInterval: number
|
||||
private reconnectCountdownInterval = 0
|
||||
private websocketFailureCount = 0
|
||||
readonly socket: Socket
|
||||
private userIsLeavingPage = false
|
||||
private externalHeartbeatInterval?: number
|
||||
@@ -69,6 +73,10 @@ export class ConnectionManager extends EventTarget {
|
||||
window.addEventListener('online', () => this.onOnline())
|
||||
window.addEventListener('beforeunload', () => {
|
||||
this.userIsLeavingPage = true
|
||||
if (this.socket.socket.transport?.name === 'xhr-polling') {
|
||||
// Websockets will close automatically.
|
||||
this.socket.socket.disconnect()
|
||||
}
|
||||
})
|
||||
|
||||
const parsedURL = new URL(
|
||||
@@ -106,7 +114,7 @@ export class ConnectionManager extends EventTarget {
|
||||
}
|
||||
|
||||
socket.on('connect', () => this.onConnect())
|
||||
socket.on('disconnect', () => this.onDisconnect())
|
||||
socket.on('disconnect', (reason: string) => this.onDisconnect(reason))
|
||||
socket.on('error', () => this.onConnectError())
|
||||
socket.on('connect_failed', () => this.onConnectError())
|
||||
socket.on('joinProjectResponse', body => this.onJoinProjectResponse(body))
|
||||
@@ -210,6 +218,9 @@ export class ConnectionManager extends EventTarget {
|
||||
}
|
||||
|
||||
private onConnectError() {
|
||||
if (this.socket.socket.transport?.name === 'websocket') {
|
||||
this.websocketFailureCount++
|
||||
}
|
||||
if (this.connectionAttempt === null) return // ignore errors once connected.
|
||||
if (this.connectionAttempt++ < MAX_RETRY_CONNECT) {
|
||||
setTimeout(
|
||||
@@ -242,9 +253,12 @@ export class ConnectionManager extends EventTarget {
|
||||
15_000
|
||||
)
|
||||
}
|
||||
// Reset on success regardless of transport. We want to upgrade back to websocket on reconnect.
|
||||
this.websocketFailureCount = 0
|
||||
}
|
||||
|
||||
private onDisconnect() {
|
||||
private onDisconnect(reason: string) {
|
||||
if (reason === BEFORE_RECONNECT) return // triggered from reconnect, ignore.
|
||||
this.connectionAttempt = null
|
||||
if (this.externalHeartbeatInterval) {
|
||||
window.clearInterval(this.externalHeartbeatInterval)
|
||||
@@ -406,6 +420,15 @@ export class ConnectionManager extends EventTarget {
|
||||
})
|
||||
|
||||
this.addReconnectListeners()
|
||||
this.socket.socket.transports = ['xhr-polling']
|
||||
if (this.websocketFailureCount < RETRY_WEBSOCKET) {
|
||||
this.socket.socket.transports.unshift('websocket')
|
||||
}
|
||||
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.socket.connect()
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,15 @@ export type Socket = {
|
||||
): void
|
||||
socket: {
|
||||
connected: boolean
|
||||
connecting: boolean
|
||||
connect(): void
|
||||
onDisconnect(reason: string): void
|
||||
disconnect(): void
|
||||
sessionid: string
|
||||
transport?: {
|
||||
name: string
|
||||
}
|
||||
transports: string[]
|
||||
}
|
||||
disconnect(): void
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ class SocketShimBase {
|
||||
const transparentMethods = [
|
||||
'connect',
|
||||
'disconnect',
|
||||
'onDisconnect',
|
||||
'emit',
|
||||
'on',
|
||||
'removeListener',
|
||||
|
||||
Reference in New Issue
Block a user