diff options
Diffstat (limited to 'language-web/src/main/js/editor')
-rw-r--r-- | language-web/src/main/js/editor/XtextWebSocketClient.ts | 47 |
1 files changed, 29 insertions, 18 deletions
diff --git a/language-web/src/main/js/editor/XtextWebSocketClient.ts b/language-web/src/main/js/editor/XtextWebSocketClient.ts index f930160a..c034f8c8 100644 --- a/language-web/src/main/js/editor/XtextWebSocketClient.ts +++ b/language-web/src/main/js/editor/XtextWebSocketClient.ts | |||
@@ -16,7 +16,7 @@ const WEBSOCKET_CLOSE_OK = 1000; | |||
16 | 16 | ||
17 | const RECONNECT_DELAY_MS = 1000; | 17 | const RECONNECT_DELAY_MS = 1000; |
18 | 18 | ||
19 | const IDLE_TIMEOUT_MS = 10 * 60 * 1000; | 19 | const BACKGROUND_IDLE_TIMEOUT_MS = 5 * 60 * 1000; |
20 | 20 | ||
21 | const PING_TIMEOUT_MS = 10 * 1000; | 21 | const PING_TIMEOUT_MS = 10 * 1000; |
22 | 22 | ||
@@ -48,6 +48,9 @@ export class XtextWebSocketClient { | |||
48 | constructor(onReconnect: ReconnectHandler, onPush: PushHandler) { | 48 | constructor(onReconnect: ReconnectHandler, onPush: PushHandler) { |
49 | this.onReconnect = onReconnect; | 49 | this.onReconnect = onReconnect; |
50 | this.onPush = onPush; | 50 | this.onPush = onPush; |
51 | document.addEventListener('visibilitychange', () => { | ||
52 | this.scheduleIdleTimeout(); | ||
53 | }); | ||
51 | this.reconnect(); | 54 | this.reconnect(); |
52 | } | 55 | } |
53 | 56 | ||
@@ -79,6 +82,8 @@ export class XtextWebSocketClient { | |||
79 | return; | 82 | return; |
80 | } | 83 | } |
81 | log.info('Connected to xtext web services'); | 84 | log.info('Connected to xtext web services'); |
85 | this.scheduleIdleTimeout(); | ||
86 | this.schedulePingTimeout(); | ||
82 | this.onReconnect(); | 87 | this.onReconnect(); |
83 | }); | 88 | }); |
84 | this.connection.addEventListener('error', (event) => { | 89 | this.connection.addEventListener('error', (event) => { |
@@ -94,18 +99,29 @@ export class XtextWebSocketClient { | |||
94 | } | 99 | } |
95 | this.cleanupAndMaybeReconnect(); | 100 | this.cleanupAndMaybeReconnect(); |
96 | }); | 101 | }); |
97 | this.scheduleIdleTimeout(); | ||
98 | this.schedulePingTimeout(); | ||
99 | } | 102 | } |
100 | 103 | ||
101 | private scheduleIdleTimeout() { | 104 | private scheduleIdleTimeout() { |
102 | if (this.idleTimeout !== null) { | 105 | if (document.visibilityState === 'hidden') { |
103 | clearTimeout(this.idleTimeout); | 106 | if (this.idleTimeout !== null) { |
107 | return; | ||
108 | } | ||
109 | log.info('Lost visibility, will disconnect in', BACKGROUND_IDLE_TIMEOUT_MS, 'ms'); | ||
110 | this.idleTimeout = setTimeout(() => { | ||
111 | this.idleTimeout = null; | ||
112 | if (!this.isClosed && document.visibilityState === 'hidden') { | ||
113 | log.info('Closing websocket connection due to inactivity'); | ||
114 | this.close(); | ||
115 | } | ||
116 | }, BACKGROUND_IDLE_TIMEOUT_MS); | ||
117 | } else { | ||
118 | log.info('Gained visibility, connection will be kept alive'); | ||
119 | if (this.idleTimeout !== null) { | ||
120 | clearTimeout(this.idleTimeout); | ||
121 | this.idleTimeout = null; | ||
122 | } | ||
123 | this.ensureOpen(); | ||
104 | } | 124 | } |
105 | this.idleTimeout = setTimeout(() => { | ||
106 | log.info('Closing websocket connection due to inactivity'); | ||
107 | this.close(); | ||
108 | }, IDLE_TIMEOUT_MS); | ||
109 | } | 125 | } |
110 | 126 | ||
111 | private schedulePingTimeout() { | 127 | private schedulePingTimeout() { |
@@ -120,17 +136,17 @@ export class XtextWebSocketClient { | |||
120 | const ping = nanoid(); | 136 | const ping = nanoid(); |
121 | log.trace('ping:', ping); | 137 | log.trace('ping:', ping); |
122 | this.pingTimeout = null; | 138 | this.pingTimeout = null; |
123 | this.internalSend({ | 139 | this.send({ |
124 | ping, | 140 | ping, |
125 | }).catch((error) => { | ||
126 | log.error('ping error', error); | ||
127 | this.forceReconnectDueToError(); | ||
128 | }).then((result) => { | 141 | }).then((result) => { |
129 | if (!isPongResult(result) || result.pong !== ping) { | 142 | if (!isPongResult(result) || result.pong !== ping) { |
130 | log.error('invalid pong'); | 143 | log.error('invalid pong'); |
131 | this.forceReconnectDueToError(); | 144 | this.forceReconnectDueToError(); |
132 | } | 145 | } |
133 | log.trace('pong:', ping); | 146 | log.trace('pong:', ping); |
147 | }).catch((error) => { | ||
148 | log.error('ping error', error); | ||
149 | this.forceReconnectDueToError(); | ||
134 | }); | 150 | }); |
135 | } | 151 | } |
136 | this.schedulePingTimeout(); | 152 | this.schedulePingTimeout(); |
@@ -179,11 +195,6 @@ export class XtextWebSocketClient { | |||
179 | if (!this.isOpen) { | 195 | if (!this.isOpen) { |
180 | throw new Error('Connection is not open'); | 196 | throw new Error('Connection is not open'); |
181 | } | 197 | } |
182 | this.scheduleIdleTimeout(); | ||
183 | return this.internalSend(request); | ||
184 | } | ||
185 | |||
186 | private internalSend(request: unknown): Promise<unknown> { | ||
187 | const messageId = this.nextMessageId.toString(16); | 198 | const messageId = this.nextMessageId.toString(16); |
188 | if (messageId in this.pendingRequests) { | 199 | if (messageId in this.pendingRequests) { |
189 | log.error('Message id wraparound still pending', messageId); | 200 | log.error('Message id wraparound still pending', messageId); |