Parcourir la source

Implement fast session recovery

When the connection to the app is already lost but there was no explicit
closure, we previously had to wait until the SaltyRTC server or the
underlying transport recognised that the connection has been lost.

With this change, we request a connectionAck and send a push after 3
seconds of silence (no incoming chunks). This has a cooldown phase of
10 seconds.
Lennart Grahl il y a 6 ans
Parent
commit
191807d591
1 fichiers modifiés avec 63 ajouts et 2 suppressions
  1. 63 2
      src/services/webclient.ts

+ 63 - 2
src/services/webclient.ts

@@ -227,6 +227,8 @@ export class WebClientService {
     private readonly pushSessionExpectedPeriodMaxMs: number;
     private pushPromise: Promise<any> | null = null;
     private deviceUnreachableDialog: ng.IPromise<any> | null = null;
+    private pushTimer: number | null = null;
+    private schedulePushAfterCooldown: boolean = false;
 
     // Timeouts
     private batteryStatusTimeout: ng.IPromise<void> = null;
@@ -547,7 +549,6 @@ export class WebClientService {
         // Once the connection is established, if this is a WebRTC connection,
         // initiate the peer connection and start the handover.
         this.salty.once('state-change:task', () => {
-
             // Determine chosen task
             const task = this.salty.getTask();
             if (task.getName().indexOf('webrtc.tasks.saltyrtc.org') !== -1) {
@@ -824,6 +825,51 @@ export class WebClientService {
         }
     }
 
+    /**
+     * Schedule a push to be sent if there is no network activity within a
+     * specified interval.
+     */
+    private schedulePush(timeoutMs: number = 3000): void {
+        if (this.pushTimer !== null) {
+            this.schedulePushAfterCooldown = true;
+            return;
+        }
+
+        // Send a push after the timeout
+        this.pushTimer = self.setTimeout(() => {
+            this.pushTimer = null;
+            this.schedulePushAfterCooldown = false;
+            this.$log.debug(this.logTag, 'Connection appears to be lost, sending push');
+            this.sendPush();
+        }, timeoutMs);
+
+        // Send a connection ack.
+        // Note: This acts as a *ping* but also helps us to keep the caches
+        //       clean.
+        this._requestConnectionAck();
+    }
+
+    /**
+     * Cancel a scheduled push.
+     */
+    private cancelPush(cooldownMs: number = 10000): void {
+        if (this.pushTimer !== null) {
+            self.clearTimeout(this.pushTimer);
+            this.pushTimer = null;
+        }
+        this.schedulePushAfterCooldown = false;
+
+        // Start the cooldown of the push timeout (if required)
+        if (cooldownMs > 0) {
+            this.pushTimer = self.setTimeout(() => {
+                this.pushTimer = null;
+                if (this.schedulePushAfterCooldown) {
+                    this.schedulePush();
+                }
+            }, cooldownMs);
+        }
+    }
+
     /**
      * For the WebRTC task, this is called when the DataChannel is open.
      * For the relayed data task, this is called once the connection is established.
@@ -1191,11 +1237,15 @@ export class WebClientService {
                 {reason: args.reason});
         }
 
-        // Stop ack timer
+        // Stop timer
         if (this.ackTimer !== null) {
             self.clearTimeout(this.ackTimer);
             this.ackTimer = null;
         }
+        if (this.pushTimer !== null) {
+            this.cancelPush(0);
+        }
+        this.$log.debug(this.logTag, 'Timer stopped');
 
         // Reset states
         this.stateService.reset(args.connectionBuildupState);
@@ -3918,7 +3968,15 @@ export class WebClientService {
             if (this.config.DEBUG && this.config.MSG_DEBUGGING) {
                 this.$log.debug(`[Chunk] Sending chunk (retransmit/push=${retransmit}:`, chunk);
             }
+
+            // Send chunk
             this.relayedDataTask.sendMessage(chunk.buffer);
+
+            // Send a push if no incoming chunks within the next two seconds.
+            // Note: This has a cooldown phase of 10 seconds.
+            if (retransmit && this.startupDone) {
+                this.schedulePush();
+            }
         }
     }
 
@@ -3942,6 +4000,9 @@ export class WebClientService {
         // Schedule the periodic ack timer
         this.scheduleConnectionAck();
 
+        // Cancel scheduled push since data has been received
+        this.cancelPush();
+
         // Process chunk
         // Warning: Nothing should be called after the unchunker has processed
         //          the chunk since the message event is synchronous and can