diff options
Diffstat (limited to 'subprojects/frontend/src/xtext/XtextWebSocketClient.ts')
-rw-r--r-- | subprojects/frontend/src/xtext/XtextWebSocketClient.ts | 71 |
1 files changed, 42 insertions, 29 deletions
diff --git a/subprojects/frontend/src/xtext/XtextWebSocketClient.ts b/subprojects/frontend/src/xtext/XtextWebSocketClient.ts index ceb1f3fd..60bf6ba9 100644 --- a/subprojects/frontend/src/xtext/XtextWebSocketClient.ts +++ b/subprojects/frontend/src/xtext/XtextWebSocketClient.ts | |||
@@ -17,6 +17,8 @@ const XTEXT_SUBPROTOCOL_V1 = 'tools.refinery.language.web.xtext.v1'; | |||
17 | 17 | ||
18 | const WEBSOCKET_CLOSE_OK = 1000; | 18 | const WEBSOCKET_CLOSE_OK = 1000; |
19 | 19 | ||
20 | const WEBSOCKET_CLOSE_GOING_AWAY = 1001; | ||
21 | |||
20 | const RECONNECT_DELAY_MS = [200, 1000, 5000, 30_000]; | 22 | const RECONNECT_DELAY_MS = [200, 1000, 5000, 30_000]; |
21 | 23 | ||
22 | const MAX_RECONNECT_DELAY_MS = | 24 | const MAX_RECONNECT_DELAY_MS = |
@@ -44,9 +46,9 @@ enum State { | |||
44 | Opening, | 46 | Opening, |
45 | TabVisible, | 47 | TabVisible, |
46 | TabHiddenIdle, | 48 | TabHiddenIdle, |
47 | TabHiddenWaiting, | 49 | TabHiddenWaitingToClose, |
48 | Error, | 50 | Error, |
49 | TimedOut, | 51 | ClosedDueToInactivity, |
50 | } | 52 | } |
51 | 53 | ||
52 | export default class XtextWebSocketClient { | 54 | export default class XtextWebSocketClient { |
@@ -86,14 +88,16 @@ export default class XtextWebSocketClient { | |||
86 | } | 88 | } |
87 | 89 | ||
88 | private get isLogicallyClosed(): boolean { | 90 | private get isLogicallyClosed(): boolean { |
89 | return this.state === State.Error || this.state === State.TimedOut; | 91 | return ( |
92 | this.state === State.Error || this.state === State.ClosedDueToInactivity | ||
93 | ); | ||
90 | } | 94 | } |
91 | 95 | ||
92 | get isOpen(): boolean { | 96 | get isOpen(): boolean { |
93 | return ( | 97 | return ( |
94 | this.state === State.TabVisible || | 98 | this.state === State.TabVisible || |
95 | this.state === State.TabHiddenIdle || | 99 | this.state === State.TabHiddenIdle || |
96 | this.state === State.TabHiddenWaiting | 100 | this.state === State.TabHiddenWaitingToClose |
97 | ); | 101 | ); |
98 | } | 102 | } |
99 | 103 | ||
@@ -134,11 +138,15 @@ export default class XtextWebSocketClient { | |||
134 | this.handleMessage(event.data); | 138 | this.handleMessage(event.data); |
135 | }); | 139 | }); |
136 | this.connection.addEventListener('close', (event) => { | 140 | this.connection.addEventListener('close', (event) => { |
137 | if ( | 141 | const closedOnRequest = |
138 | this.isLogicallyClosed && | 142 | this.isLogicallyClosed && |
139 | event.code === WEBSOCKET_CLOSE_OK && | 143 | event.code === WEBSOCKET_CLOSE_OK && |
140 | this.pendingRequests.size === 0 | 144 | this.pendingRequests.size === 0; |
141 | ) { | 145 | const closedOnNavigation = event.code === WEBSOCKET_CLOSE_GOING_AWAY; |
146 | if (closedOnNavigation) { | ||
147 | this.state = State.ClosedDueToInactivity; | ||
148 | } | ||
149 | if (closedOnRequest || closedOnNavigation) { | ||
142 | log.info('Websocket closed'); | 150 | log.info('Websocket closed'); |
143 | return; | 151 | return; |
144 | } | 152 | } |
@@ -157,12 +165,12 @@ export default class XtextWebSocketClient { | |||
157 | this.idleTimer.cancel(); | 165 | this.idleTimer.cancel(); |
158 | if ( | 166 | if ( |
159 | this.state === State.TabHiddenIdle || | 167 | this.state === State.TabHiddenIdle || |
160 | this.state === State.TabHiddenWaiting | 168 | this.state === State.TabHiddenWaitingToClose |
161 | ) { | 169 | ) { |
162 | this.handleTabVisibleConnected(); | 170 | this.handleTabVisibleConnected(); |
163 | return; | 171 | return; |
164 | } | 172 | } |
165 | if (this.state === State.TimedOut) { | 173 | if (this.state === State.ClosedDueToInactivity) { |
166 | this.reconnect(); | 174 | this.reconnect(); |
167 | } | 175 | } |
168 | } | 176 | } |
@@ -181,19 +189,19 @@ export default class XtextWebSocketClient { | |||
181 | private handleIdleTimeout() { | 189 | private handleIdleTimeout() { |
182 | log.trace('Waiting for pending tasks before disconnect'); | 190 | log.trace('Waiting for pending tasks before disconnect'); |
183 | if (this.state === State.TabHiddenIdle) { | 191 | if (this.state === State.TabHiddenIdle) { |
184 | this.state = State.TabHiddenWaiting; | 192 | this.state = State.TabHiddenWaitingToClose; |
185 | this.handleWaitingForDisconnect(); | 193 | this.handleWaitingForDisconnect(); |
186 | } | 194 | } |
187 | } | 195 | } |
188 | 196 | ||
189 | private handleWaitingForDisconnect() { | 197 | private handleWaitingForDisconnect() { |
190 | if (this.state !== State.TabHiddenWaiting) { | 198 | if (this.state !== State.TabHiddenWaitingToClose) { |
191 | return; | 199 | return; |
192 | } | 200 | } |
193 | const pending = this.pendingRequests.size; | 201 | const pending = this.pendingRequests.size; |
194 | if (pending === 0) { | 202 | if (pending === 0) { |
195 | log.info('Closing idle websocket'); | 203 | log.info('Closing idle websocket'); |
196 | this.state = State.TimedOut; | 204 | this.state = State.ClosedDueToInactivity; |
197 | this.closeConnection(1000, 'idle timeout'); | 205 | this.closeConnection(1000, 'idle timeout'); |
198 | return; | 206 | return; |
199 | } | 207 | } |
@@ -334,17 +342,31 @@ export default class XtextWebSocketClient { | |||
334 | if (this.isLogicallyClosed) { | 342 | if (this.isLogicallyClosed) { |
335 | return; | 343 | return; |
336 | } | 344 | } |
337 | this.abortPendingRequests(); | ||
338 | this.closeConnection(1000, 'reconnecting due to error'); | ||
339 | log.error('Reconnecting after delay due to error'); | ||
340 | this.handleErrorState(); | ||
341 | } | ||
342 | |||
343 | private abortPendingRequests() { | ||
344 | this.pendingRequests.forEach((request) => { | 345 | this.pendingRequests.forEach((request) => { |
345 | request.reject(new Error('Websocket disconnect')); | 346 | request.reject(new Error('Websocket disconnect')); |
346 | }); | 347 | }); |
347 | this.pendingRequests.clear(); | 348 | this.pendingRequests.clear(); |
349 | this.closeConnection(1000, 'reconnecting due to error'); | ||
350 | if (this.state === State.Error) { | ||
351 | // We are already handling this error condition. | ||
352 | return; | ||
353 | } | ||
354 | if ( | ||
355 | this.state === State.TabHiddenIdle || | ||
356 | this.state === State.TabHiddenWaitingToClose | ||
357 | ) { | ||
358 | log.error('Will reconned due to error once the tab becomes visible'); | ||
359 | this.idleTimer.cancel(); | ||
360 | this.state = State.ClosedDueToInactivity; | ||
361 | return; | ||
362 | } | ||
363 | log.error('Reconnecting after delay due to error'); | ||
364 | this.state = State.Error; | ||
365 | this.reconnectTryCount += 1; | ||
366 | const delay = | ||
367 | RECONNECT_DELAY_MS[this.reconnectTryCount - 1] ?? MAX_RECONNECT_DELAY_MS; | ||
368 | log.info('Reconnecting in', delay, 'ms'); | ||
369 | this.reconnectTimer.schedule(delay); | ||
348 | } | 370 | } |
349 | 371 | ||
350 | private closeConnection(code: number, reason: string) { | 372 | private closeConnection(code: number, reason: string) { |
@@ -355,22 +377,13 @@ export default class XtextWebSocketClient { | |||
355 | } | 377 | } |
356 | } | 378 | } |
357 | 379 | ||
358 | private handleErrorState() { | ||
359 | this.state = State.Error; | ||
360 | this.reconnectTryCount += 1; | ||
361 | const delay = | ||
362 | RECONNECT_DELAY_MS[this.reconnectTryCount - 1] || MAX_RECONNECT_DELAY_MS; | ||
363 | log.info('Reconnecting in', delay, 'ms'); | ||
364 | this.reconnectTimer.schedule(delay); | ||
365 | } | ||
366 | |||
367 | private handleReconnect() { | 380 | private handleReconnect() { |
368 | if (this.state !== State.Error) { | 381 | if (this.state !== State.Error) { |
369 | log.error('Unexpected reconnect in', this.state); | 382 | log.error('Unexpected reconnect in', this.state); |
370 | return; | 383 | return; |
371 | } | 384 | } |
372 | if (document.visibilityState === 'hidden') { | 385 | if (document.visibilityState === 'hidden') { |
373 | this.state = State.TimedOut; | 386 | this.state = State.ClosedDueToInactivity; |
374 | } else { | 387 | } else { |
375 | this.reconnect(); | 388 | this.reconnect(); |
376 | } | 389 | } |