소스 검색

Multi step soft reconnect

Reset the timeout duration after every important connection buildup
state change.
Danilo Bargen 9 년 전
부모
커밋
74d183d96b
5개의 변경된 파일70개의 추가작업 그리고 18개의 파일을 삭제
  1. 41 14
      src/controllers/status.ts
  2. 5 0
      src/partials/welcome.ts
  3. 8 1
      src/services/state.ts
  4. 11 1
      src/services/webclient.ts
  5. 5 2
      src/threema.d.ts

+ 41 - 14
src/controllers/status.ts

@@ -28,6 +28,8 @@ import {WebClientService} from '../services/webclient';
  */
 export class StatusController {
 
+    private logTag: string = '[StatusController]';
+
     // State variable
     private state: threema.GlobalConnectionState = 'error';
 
@@ -110,7 +112,7 @@ export class StatusController {
                 }
                 break;
             default:
-                this.$log.error('Invalid state change: From', oldValue, 'to', newValue);
+                this.$log.error(this.logTag, 'Invalid state change: From', oldValue, 'to', newValue);
         }
     }
 
@@ -137,12 +139,19 @@ export class StatusController {
      * Go back to the welcome screen and try to reconnect using the same keys as right now.
      */
     private reconnect(): void {
-        this.$log.debug('Connection lost. Attempting to reconnect...');
+        this.$log.warn(this.logTag, 'Connection lost. Attempting to reconnect...');
 
         // Get original keys
         let originalKeyStore = this.webClientService.salty.keyStore;
         let originalPeerPermanentKeyBytes = this.webClientService.salty.peerPermanentKeyBytes;
 
+        // Timeout durations
+        const TIMEOUT1 = 20 * 1000; // Duration per step for first reconnect
+        const TIMEOUT2 = 20 * 1000; // Duration per step for second reconnect
+
+        // Reconnect state
+        let reconnectTry: 1 | 2 = 1;
+
         // Handler for failed reconnection attempts
         let reconnectionFailed = () => {
             // Collapse status bar
@@ -160,6 +169,20 @@ export class StatusController {
             });
         };
 
+        // Handlers for reconnecting timeout
+        const reconnect2Timeout = () => {
+            // Give up
+            this.$log.error(this.logTag, 'Reconnect timeout 2. Going back to initial loading screen...');
+            reconnectionFailed();
+        };
+        const reconnect1Timeout = () => {
+            // Could not connect so far.
+            this.$log.error(this.logTag, 'Reconnect timeout 1. Retrying...');
+            reconnectTry = 2;
+            this.reconnectTimeout = this.$timeout(reconnect2Timeout, TIMEOUT2);
+            doSoftReconnect();
+        };
+
         // Function to soft-reconnect. Does not reset the loaded data.
         let doSoftReconnect = () => {
             const deleteStoredData = false;
@@ -176,25 +199,29 @@ export class StatusController {
                     this.collapseStatusBar();
                 },
                 (error) => {
-                    this.$log.error('Error state:', error);
+                    this.$log.error(this.logTag, 'Error state:', error);
                     this.$timeout.cancel(this.reconnectTimeout);
                     reconnectionFailed();
                 },
+                (progress: threema.ConnectionBuildupStateChange) => {
+                    if (progress.state === 'peer_handshake' || progress.state === 'loading') {
+                        this.$log.debug(this.logTag, 'Connection buildup advanced, resetting timeout');
+                        // Restart timeout
+                        this.$timeout.cancel(this.reconnectTimeout);
+                        if (reconnectTry === 1) {
+                            this.reconnectTimeout = this.$timeout(reconnect1Timeout, TIMEOUT1);
+                        } else if (reconnectTry === 2) {
+                            this.reconnectTimeout = this.$timeout(reconnect2Timeout, TIMEOUT2);
+                        } else {
+                            throw new Error('Invalid reconnectTry value: ' + reconnectTry);
+                        }
+                    }
+                },
             );
         };
 
         // Start timeout
-        this.reconnectTimeout = this.$timeout(() => {
-            // Could not connect so far.
-            // Retry once, but increase reconnect timeout to 40 seconds
-            this.$log.error('Reconnect timeout 1. Retrying for 40 seconds...');
-            this.reconnectTimeout = this.$timeout(() => {
-                // Give up
-                this.$log.error('Reconnect timeout 2. Going back to initial loading screen...');
-                reconnectionFailed();
-            }, 40000);
-            doSoftReconnect();
-        }, 20000);
+        this.reconnectTimeout = this.$timeout(reconnect1Timeout, TIMEOUT1);
 
         // Start reconnecting process
         doSoftReconnect();

+ 5 - 0
src/partials/welcome.ts

@@ -390,6 +390,11 @@ class WelcomeController {
                 // TODO: should probably show an error message instead
                 this.$timeout(() => this.$state.reload(), WelcomeController.REDIRECT_DELAY);
             },
+
+            // State updates
+            (progress: threema.ConnectionBuildupStateChange) => {
+                // Do nothing
+            },
         );
     }
 

+ 8 - 1
src/services/state.ts

@@ -15,6 +15,8 @@
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  */
 
+import {AsyncEvent} from 'ts-events';
+
 export class StateService {
 
     private logTag: string = '[StateService]';
@@ -23,6 +25,9 @@ export class StateService {
     private $log: ng.ILogService;
     private $interval: ng.IIntervalService;
 
+    // Events
+    public evtConnectionBuildupStateChange = new AsyncEvent<threema.ConnectionBuildupStateChange>();
+
     // WebRTC states
     public signalingConnectionState: saltyrtc.SignalingState;
     public rtcConnectionState: threema.RTCConnectionState;
@@ -111,10 +116,12 @@ export class StateService {
         if (this.connectionBuildupState === state) {
             return;
         }
-        this.$log.debug(this.logTag, 'Connection buildup state:', this.connectionBuildupState, '=>', state);
+        const prevState = this.connectionBuildupState;
+        this.$log.debug(this.logTag, 'Connection buildup state:', prevState, '=>', state);
 
         // Update state
         this.connectionBuildupState = state;
+        this.evtConnectionBuildupStateChange.post({state: state, prevState: prevState});
 
         // Cancel progress interval if present
         if (this.progressInterval !== null) {

+ 11 - 1
src/services/webclient.ts

@@ -145,7 +145,7 @@ export class WebClientService {
     private receiverService: ReceiverService;
 
     // State handling
-    private startupPromise: ng.IDeferred<{}>; // TODO: deferred type
+    private startupPromise: ng.IDeferred<{}> = null; // TODO: deferred type
     private startupDone: boolean = false;
     private pendingInitializationStepRoutines: threema.InitializationStepRoutine[] = [];
     private initialized: Set<threema.InitializationStep> = new Set();
@@ -257,6 +257,15 @@ export class WebClientService {
 
         // Setup fields
         this._resetFields();
+
+        // Register event handlers
+        this.stateService.evtConnectionBuildupStateChange.attach(
+            (stateChange: threema.ConnectionBuildupStateChange) => {
+                if (this.startupPromise !== null) {
+                    this.startupPromise.notify(stateChange);
+                }
+            },
+        );
     }
 
     get me(): threema.MeReceiver {
@@ -639,6 +648,7 @@ export class WebClientService {
         if (this.initialized.size >= this.initializedThreshold) {
             this.stateService.updateConnectionBuildupState('done');
             this.startupPromise.resolve();
+            this.startupPromise = null;
             this.startupDone = true;
             this._resetInitializationSteps();
         }

+ 5 - 2
src/threema.d.ts

@@ -15,8 +15,6 @@
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  */
 
-// Types used for the Threema Webclient.
-
 declare const angular: ng.IAngularStatic;
 
 declare namespace threema {
@@ -248,6 +246,11 @@ declare namespace threema {
     type ConnectionBuildupState = 'new' | 'connecting' | 'push' | 'manual_start' | 'waiting'
         | 'peer_handshake' | 'loading' | 'done' | 'closed';
 
+    interface ConnectionBuildupStateChange {
+        state: ConnectionBuildupState;
+        prevState: ConnectionBuildupState;
+    }
+
     /**
      * Connection state of the WebRTC peer connection.
      */