소스 검색

Handle handover and incoming messages

Danilo Bargen 7 년 전
부모
커밋
db4354d316
2개의 변경된 파일106개의 추가작업 그리고 61개의 파일을 삭제
  1. 100 61
      src/services/webclient.ts
  2. 6 0
      src/threema.d.ts

+ 100 - 61
src/services/webclient.ts

@@ -166,6 +166,7 @@ export class WebClientService {
     private webrtcTask: saltyrtc.tasks.webrtc.WebRTCTask = null;
     private relayedDataTask: saltyrtc.tasks.relayed_data.RelayedDataTask = null;
     private secureDataChannel: saltyrtc.tasks.webrtc.SecureDataChannel = null;
+    private chosenTask: threema.ChosenTask = threema.ChosenTask.None;
 
     // Messenger data
     public messages: threema.Container.Messages;
@@ -412,33 +413,45 @@ 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', () => {
-            if (this.salty.getTask().getName().indexOf('webrtc.tasks.saltyrtc.org') === -1) {
-                // This is not a WebRTC task, so no handover!
-                return;
+            // Determine chosen task
+            const task = this.salty.getTask();
+            if (task.getName().indexOf('webrtc.tasks.saltyrtc.org') !== -1) {
+                this.chosenTask = threema.ChosenTask.WebRTC;
+            } else if (task.getName().indexOf('relayed-data.tasks.saltyrtc.org') !== -1) {
+                this.chosenTask = threema.ChosenTask.RelayedData;
+            } else {
+                throw new Error('Invalid or unknown task name: ' + task.getName());
             }
 
-            // Firefox <53 does not yet support TLS. Skip it, to save allocations.
-            const browser = this.browserService.getBrowser();
-            if (browser.firefox && parseFloat(browser.version) < 53) {
-                this.skipIceTls();
-            }
+            // If the WebRTC task was chosen, initialize handover.
+            if (this.chosenTask === threema.ChosenTask.WebRTC) {
+                // Firefox <53 does not yet support TLS. Skip it, to save allocations.
+                const browser = this.browserService.getBrowser();
+                if (browser.firefox && parseFloat(browser.version) < 53) {
+                    this.skipIceTls();
+                }
 
-            this.pcHelper = new PeerConnectionHelper(this.$log, this.$q, this.$timeout,
-                                                     this.$rootScope, this.webrtcTask,
-                                                     this.config.ICE_SERVERS,
-                                                     !this.config.ICE_DEBUGGING);
+                this.pcHelper = new PeerConnectionHelper(this.$log, this.$q, this.$timeout,
+                    this.$rootScope, this.webrtcTask,
+                    this.config.ICE_SERVERS,
+                    !this.config.ICE_DEBUGGING);
 
-            // On state changes in the PeerConnectionHelper class, let state service know about it
-            this.pcHelper.onConnectionStateChange = (state: threema.RTCConnectionState) => {
-                this.stateService.updateRtcConnectionState(state);
-            };
+                // On state changes in the PeerConnectionHelper class, let state service know about it
+                this.pcHelper.onConnectionStateChange = (state: threema.RTCConnectionState) => {
+                    this.stateService.updateRtcConnectionState(state);
+                };
+
+                // Initiate handover
+                this.webrtcTask.handover(this.pcHelper.peerConnection);
 
-            // Initiate handover
-            this.webrtcTask.handover(this.pcHelper.peerConnection);
+            // Otherwise, no handover is necessary.
+            } else {
+                this.onHandover(resetFields);
+                return;
+            }
         });
 
         // Handle a disconnect request
-        // TODO: Handle this from the relayed data task too!
         this.salty.on('application', (applicationData: any) => {
             if (applicationData.data.type === 'disconnect') {
                 this.$log.debug(this.logTag, 'Disconnecting requested');
@@ -452,11 +465,31 @@ export class WebClientService {
         // Wait for handover to be finished
         this.salty.on('handover', () => {
             this.$log.debug(this.logTag, 'Handover done');
+            this.onHandover(resetFields);
+        });
 
-            // Initialize NotificationService
-            this.$log.debug(this.logTag, 'Initializing NotificationService...');
-            this.notificationService.init();
+        // Handle SaltyRTC errors
+        this.salty.on('connection-error', (ev) => {
+            this.$log.error('Connection error:', ev);
+        });
+        this.salty.on('connection-closed', (ev) => {
+            this.$log.warn('Connection closed:', ev);
+        });
+    }
+
+    /**
+     * Handover done.
+     *
+     * This can either be a real handover to WebRTC (Android), or simply
+     * when the relayed data task takes over (iOS).
+     */
+    private onHandover = (resetFields: boolean) => {
+        // Initialize NotificationService
+        this.$log.debug(this.logTag, 'Initializing NotificationService...');
+        this.notificationService.init();
 
+        // If the WebRTC task was chosen, initialize the data channel
+        if (this.chosenTask === threema.ChosenTask.WebRTC) {
             // Create secure data channel
             this.$log.debug(this.logTag, 'Create SecureDataChannel "' + WebClientService.DC_LABEL + '"...');
             this.secureDataChannel = this.pcHelper.createSecureDataChannel(
@@ -494,54 +527,28 @@ export class WebClientService {
             );
 
             // Handle incoming messages
-            type RTCMessageEvent = (event: MessageEvent) => void;
-            this.secureDataChannel.onmessage = (event: MessageEvent) => {
-                this.$log.debug('New incoming message (' + event.data.byteLength + ' bytes)');
-
-                // Decode bytes
-                const bytes = new Uint8Array(event.data);
-                const message: threema.WireMessage = this.msgpackDecode(bytes);
-
-                // Validate message to keep contract defined by `threema.WireMessage` type
-                if (message.type === undefined) {
-                    this.$log.warn('Ignoring invalid message (no type attribute)');
-                    return;
-                } else if (message.subType === undefined) {
-                    this.$log.warn('Ignoring invalid message (no subType attribute)');
-                    return;
-                }
-
-                if (this.config.MSG_DEBUGGING) {
-                    // Deep copy message to prevent issues with JS debugger
-                    const deepcopy = JSON.parse(JSON.stringify(message));
-                    this.$log.debug('[Message] Incoming:', message.type, '/', message.subType, deepcopy);
-                }
-
-                // Process data
-                this.$rootScope.$apply(() => {
-                    this.receive(message);
-                });
+            type RTCMessageEvent = (ev: MessageEvent) => void;
+            this.secureDataChannel.onmessage = (ev: MessageEvent) => {
+                const bytes = new Uint8Array(ev.data);
+                this.handleIncomingMessage(bytes);
             };
-            this.secureDataChannel.onbufferedamountlow = (e: Event) => {
+            this.secureDataChannel.onbufferedamountlow = (ev: Event) => {
                 this.$log.debug('Secure data channel: Buffered amount low');
             };
             this.secureDataChannel.onerror = (e: ErrorEvent) => {
                 this.$log.warn('Secure data channel: Error:', e.message);
                 this.$log.debug(e);
             };
-            this.secureDataChannel.onclose = (e: Event) => {
+            this.secureDataChannel.onclose = (ev: Event) => {
                 this.$log.warn('Secure data channel: Closed');
             };
-        });
-
-        // Handle SaltyRTC errors
-        this.salty.on('connection-error', (ev) => {
-            this.$log.error('Connection error:', ev);
-        });
-        this.salty.on('connection-closed', (ev) => {
-            this.$log.warn('Connection closed:', ev);
-        });
-    }
+        } else if (this.chosenTask === threema.ChosenTask.RelayedData) {
+            // Handle messages directly
+            this.relayedDataTask.on('data', (ev: saltyrtc.SaltyRTCEvent) => {
+                this.handleIncomingMessage(ev.data);
+            });
+        }
+    };
 
     /**
      * Start the webclient service.
@@ -2523,6 +2530,38 @@ export class WebClientService {
         this.secureDataChannel.send(bytes);
     }
 
+    /**
+     * Handle message bytes incoming through the SecureDataChannel
+     * or through the relayed data WebSocket.
+     */
+    private handleIncomingMessage(bytes: Uint8Array): void {
+        this.$log.debug('New incoming message (' + bytes.byteLength + ' bytes)');
+
+        // Decode bytes
+        const message: threema.WireMessage = this.msgpackDecode(bytes);
+
+        // Validate message to keep contract defined by `threema.WireMessage` type
+        if (message.type === undefined) {
+            this.$log.warn('Ignoring invalid message (no type attribute)');
+            return;
+        } else if (message.subType === undefined) {
+            this.$log.warn('Ignoring invalid message (no subType attribute)');
+            return;
+        }
+
+        // If desired, log message type / subtype
+        if (this.config.MSG_DEBUGGING) {
+            // Deep copy message to prevent issues with JS debugger
+            const deepcopy = JSON.parse(JSON.stringify(message));
+            this.$log.debug('[Message] Incoming:', message.type, '/', message.subType, deepcopy);
+        }
+
+        // Process data
+        this.$rootScope.$apply(() => {
+            this.receive(message);
+        });
+    }
+
     /**
      * Receive a new incoming decrypted message.
      */

+ 6 - 0
src/threema.d.ts

@@ -503,6 +503,12 @@ declare namespace threema {
         realLength: number;
     }
 
+    const enum ChosenTask {
+        None = 'none',
+        WebRTC = 'webrtc',
+        RelayedData = 'relayed-data',
+    }
+
     namespace Container {
         interface ReceiverData {
             me: MeReceiver;