Improved WS connection failure handling
This commit is contained in:
parent
7f49ead439
commit
97cda14a04
@ -1,8 +1,7 @@
|
|||||||
import { ApplicationRef, Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { filter, take } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { ConfigService } from './core/ui-services/config.service';
|
import { ConfigService } from './core/ui-services/config.service';
|
||||||
import { ConstantsService } from './core/core-services/constants.service';
|
import { ConstantsService } from './core/core-services/constants.service';
|
||||||
@ -65,7 +64,6 @@ export class AppComponent {
|
|||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
appRef: ApplicationRef,
|
|
||||||
servertimeService: ServertimeService,
|
servertimeService: ServertimeService,
|
||||||
router: Router,
|
router: Router,
|
||||||
operator: OperatorService,
|
operator: OperatorService,
|
||||||
@ -95,15 +93,7 @@ export class AppComponent {
|
|||||||
this.overloadFlatMap();
|
this.overloadFlatMap();
|
||||||
this.overloadModulo();
|
this.overloadModulo();
|
||||||
|
|
||||||
// Show the spinner initial
|
servertimeService.startScheduler();
|
||||||
|
|
||||||
appRef.isStable
|
|
||||||
.pipe(
|
|
||||||
// take only the stable state
|
|
||||||
filter(s => s),
|
|
||||||
take(1)
|
|
||||||
)
|
|
||||||
.subscribe(() => servertimeService.startScheduler());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { ApplicationRef, Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { first, take } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { ConstantsService } from './constants.service';
|
import { ConstantsService } from './constants.service';
|
||||||
import { Deferred } from '../promises/deferred';
|
import { Deferred } from '../promises/deferred';
|
||||||
@ -21,37 +19,25 @@ export class PingService {
|
|||||||
*/
|
*/
|
||||||
private pingInterval: any;
|
private pingInterval: any;
|
||||||
|
|
||||||
private intervalTime: number;
|
private intervalTime = 30000;
|
||||||
|
|
||||||
private timeoutTime: number;
|
private timeoutTime = 5000;
|
||||||
|
|
||||||
private lastLatency: number | null = null;
|
private lastLatency: number | null = null;
|
||||||
|
|
||||||
public constructor(
|
public constructor(private websocketService: WebsocketService, private constantsService: ConstantsService) {
|
||||||
private websocketService: WebsocketService,
|
|
||||||
private appRef: ApplicationRef,
|
|
||||||
private constantsService: ConstantsService
|
|
||||||
) {
|
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setup(): Promise<void> {
|
private async setup(): Promise<void> {
|
||||||
const gotConstants = new Deferred();
|
const gotConstants = new Deferred();
|
||||||
const isStable = new Deferred();
|
|
||||||
|
|
||||||
this.constantsService
|
this.constantsService.get<OpenSlidesSettings>('Settings').subscribe(settings => {
|
||||||
.get<OpenSlidesSettings>('Settings')
|
|
||||||
.pipe(take(1))
|
|
||||||
.subscribe(settings => {
|
|
||||||
this.intervalTime = settings.PING_INTERVAL || 30000;
|
this.intervalTime = settings.PING_INTERVAL || 30000;
|
||||||
this.timeoutTime = settings.PING_TIMEOUT || 5000;
|
this.timeoutTime = settings.PING_TIMEOUT || 5000;
|
||||||
gotConstants.resolve();
|
gotConstants.resolve();
|
||||||
});
|
});
|
||||||
this.appRef.isStable.pipe(first(s => s)).subscribe(() => {
|
await gotConstants;
|
||||||
isStable.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all([gotConstants, isStable]);
|
|
||||||
|
|
||||||
// Connects the ping-pong mechanism to the opening and closing of the connection.
|
// Connects the ping-pong mechanism to the opening and closing of the connection.
|
||||||
this.websocketService.closeEvent.subscribe(() => this.stopPing());
|
this.websocketService.closeEvent.subscribe(() => this.stopPing());
|
||||||
|
@ -32,7 +32,7 @@ export class ServertimeService {
|
|||||||
* Starts the scheduler to sync with the server.
|
* Starts the scheduler to sync with the server.
|
||||||
*/
|
*/
|
||||||
public startScheduler(): void {
|
public startScheduler(): void {
|
||||||
this.scheduleNextRefresh(0);
|
this.scheduleNextRefresh(0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -156,7 +156,8 @@ export class WebsocketService {
|
|||||||
/**
|
/**
|
||||||
* The websocket.
|
* The websocket.
|
||||||
*/
|
*/
|
||||||
private websocket: WebSocket;
|
private websocket: WebSocket | null;
|
||||||
|
private websocketId: string | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subjects for types of websocket messages. A subscriber can get an Observable by {@function getOberservable}.
|
* Subjects for types of websocket messages. A subscriber can get an Observable by {@function getOberservable}.
|
||||||
@ -208,8 +209,14 @@ export class WebsocketService {
|
|||||||
* Uses NgZone to let all callbacks run in the angular context.
|
* Uses NgZone to let all callbacks run in the angular context.
|
||||||
*/
|
*/
|
||||||
public async connect(options: ConnectOptions = {}, retry: boolean = false): Promise<void> {
|
public async connect(options: ConnectOptions = {}, retry: boolean = false): Promise<void> {
|
||||||
|
const websocketId = Math.random()
|
||||||
|
.toString(36)
|
||||||
|
.substring(7);
|
||||||
|
this.websocketId = websocketId;
|
||||||
|
|
||||||
if (this.websocket) {
|
if (this.websocket) {
|
||||||
await this.close();
|
this.websocket.close();
|
||||||
|
this.websocket = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!retry) {
|
if (!retry) {
|
||||||
@ -244,17 +251,20 @@ export class WebsocketService {
|
|||||||
// connection established. If this connect attept was a retry,
|
// connection established. If this connect attept was a retry,
|
||||||
// The error notice will be removed and the reconnectSubject is published.
|
// The error notice will be removed and the reconnectSubject is published.
|
||||||
this.websocket.onopen = (event: Event) => {
|
this.websocket.onopen = (event: Event) => {
|
||||||
|
if (this.websocketId !== websocketId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
this.retryCounter = 0;
|
this.retryCounter = 0;
|
||||||
|
|
||||||
if (this.shouldBeClosed) {
|
if (this.shouldBeClosed) {
|
||||||
this.dismissConnectionErrorNotice();
|
this.offlineService.goOnline();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._connectionOpen = true;
|
this._connectionOpen = true;
|
||||||
if (retry) {
|
if (retry) {
|
||||||
this.dismissConnectionErrorNotice();
|
this.offlineService.goOnline();
|
||||||
this._retryReconnectEvent.emit();
|
this._retryReconnectEvent.emit();
|
||||||
} else {
|
} else {
|
||||||
this._noRetryConnectEvent.emit();
|
this._noRetryConnectEvent.emit();
|
||||||
@ -268,22 +278,31 @@ export class WebsocketService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.websocket.onmessage = (event: MessageEvent) => {
|
this.websocket.onmessage = (event: MessageEvent) => {
|
||||||
|
if (this.websocketId !== websocketId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
this.handleMessage(event.data);
|
this.handleMessage(event.data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.websocket.onclose = (event: CloseEvent) => {
|
this.websocket.onclose = (event: CloseEvent) => {
|
||||||
|
if (this.websocketId !== websocketId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
this.onclose(event.code === 1000);
|
this.onclose();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.websocket.onerror = (event: ErrorEvent) => {
|
this.websocket.onerror = (event: ErrorEvent) => {
|
||||||
|
if (this.websocketId !== websocketId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// place for proper error handling and debugging.
|
// place for proper error handling and debugging.
|
||||||
// Required to get more information about errors
|
// Required to get more information about errors
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
console.warn('Websocket is on Error state. Error: ', event);
|
console.warn('WS error event:', event);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -353,25 +372,20 @@ export class WebsocketService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Simulates an abnormal close.
|
|
||||||
*/
|
|
||||||
public simulateAbnormalClose(): void {
|
|
||||||
if (this.websocket) {
|
|
||||||
this.websocket.close();
|
|
||||||
}
|
|
||||||
this.onclose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the connection error notice
|
* Closes the connection error notice
|
||||||
*/
|
*/
|
||||||
private onclose(normalClose: boolean): void {
|
private onclose(): void {
|
||||||
|
if (this.websocket) {
|
||||||
|
this.websocketId = null; // set to null, so now further events will be
|
||||||
|
// registered with the line below.
|
||||||
|
this.websocket.close(); // Cleanup old connection
|
||||||
this.websocket = null;
|
this.websocket = null;
|
||||||
|
}
|
||||||
this._connectionOpen = false;
|
this._connectionOpen = false;
|
||||||
// 1000 is a normal close, like the close on logout
|
// 1000 is a normal close, like the close on logout
|
||||||
this._closeEvent.emit();
|
this._closeEvent.emit();
|
||||||
if (!this.shouldBeClosed && !normalClose) {
|
if (!this.shouldBeClosed) {
|
||||||
// Do not show the message snackbar on the projector
|
// Do not show the message snackbar on the projector
|
||||||
// tests for /projector and /projector/<id>
|
// tests for /projector and /projector/<id>
|
||||||
const onProjector = this.router.url.match(/^\/projector(\/[0-9]+\/?)?$/);
|
const onProjector = this.router.url.match(/^\/projector(\/[0-9]+\/?)?$/);
|
||||||
@ -399,16 +413,12 @@ export class WebsocketService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private dismissConnectionErrorNotice(): void {
|
|
||||||
this.offlineService.goOnline();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the websocket connection.
|
* Closes the websocket connection.
|
||||||
*/
|
*/
|
||||||
public async close(): Promise<void> {
|
public async close(): Promise<void> {
|
||||||
this.shouldBeClosed = true;
|
this.shouldBeClosed = true;
|
||||||
this.dismissConnectionErrorNotice();
|
this.offlineService.goOnline();
|
||||||
if (this.websocket) {
|
if (this.websocket) {
|
||||||
this.websocket.close();
|
this.websocket.close();
|
||||||
this.websocket = null;
|
this.websocket = null;
|
||||||
@ -416,6 +426,15 @@ export class WebsocketService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates an abnormal close.
|
||||||
|
*
|
||||||
|
* Internally does not set `shouldBeClosed`, so a reconnect is forced.
|
||||||
|
*/
|
||||||
|
public simulateAbnormalClose(): void {
|
||||||
|
this.onclose();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* closes and reopens the connection. If the connection was closed before,
|
* closes and reopens the connection. If the connection was closed before,
|
||||||
* it will be just opened.
|
* it will be just opened.
|
||||||
|
Loading…
Reference in New Issue
Block a user