|  |  |  | @ -109,17 +109,14 @@ NSString *const SocketConnectingNotification = @"SocketConnectingNotification"; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Try to reuse the existing socket (if any) if it is in a valid state. | 
		
	
		
			
				|  |  |  |  |     SRWebSocket *socket = self.websocket; | 
		
	
		
			
				|  |  |  |  |     if (socket) { | 
		
	
		
			
				|  |  |  |  |         switch ([socket readyState]) { | 
		
	
		
			
				|  |  |  |  |     if (self.websocket) { | 
		
	
		
			
				|  |  |  |  |         switch ([self.websocket readyState]) { | 
		
	
		
			
				|  |  |  |  |             case SR_OPEN: | 
		
	
		
			
				|  |  |  |  |                 DDLogVerbose(@"WebSocket already open on connection request"); | 
		
	
		
			
				|  |  |  |  |                 OWSAssert(self.status == kSocketStatusOpen); | 
		
	
		
			
				|  |  |  |  |                 self.status = kSocketStatusOpen; | 
		
	
		
			
				|  |  |  |  |                 return; | 
		
	
		
			
				|  |  |  |  |             case SR_CONNECTING: | 
		
	
		
			
				|  |  |  |  |                 DDLogVerbose(@"WebSocket is already connecting"); | 
		
	
		
			
				|  |  |  |  |                 OWSAssert(self.status == kSocketStatusConnecting); | 
		
	
		
			
				|  |  |  |  |                 self.status = kSocketStatusConnecting; | 
		
	
		
			
				|  |  |  |  |                 return; | 
		
	
		
			
				|  |  |  |  |             default: | 
		
	
	
		
			
				
					|  |  |  | @ -127,22 +124,117 @@ NSString *const SocketConnectingNotification = @"SocketConnectingNotification"; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Discard the old socket which is already closed or is closing. | 
		
	
		
			
				|  |  |  |  |     [self closeWebSocket]; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     OWSAssert(self.status == kSocketStatusClosed); | 
		
	
		
			
				|  |  |  |  |     // If socket is not already open or connecting, connect now. | 
		
	
		
			
				|  |  |  |  |     self.status = kSocketStatusConnecting; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Create a new web socket. | 
		
	
		
			
				|  |  |  |  |     NSString *webSocketConnect = [textSecureWebSocketAPI stringByAppendingString:[self webSocketAuthenticationString]]; | 
		
	
		
			
				|  |  |  |  |     NSURL *webSocketConnectURL   = [NSURL URLWithString:webSocketConnect]; | 
		
	
		
			
				|  |  |  |  |     NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:webSocketConnectURL]; | 
		
	
		
			
				|  |  |  |  | - (NSString *)stringFromSocketStatus:(SocketStatus)status { | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     // If this status update is _not_ redundant, | 
		
	
		
			
				|  |  |  |  |     // update class state to reflect the new status. | 
		
	
		
			
				|  |  |  |  |     switch (status) { | 
		
	
		
			
				|  |  |  |  |         case kSocketStatusClosed: | 
		
	
		
			
				|  |  |  |  |             return @"Closed"; | 
		
	
		
			
				|  |  |  |  |         case kSocketStatusOpen: | 
		
	
		
			
				|  |  |  |  |             return @"Open"; | 
		
	
		
			
				|  |  |  |  |         case kSocketStatusConnecting: | 
		
	
		
			
				|  |  |  |  |             return @"Connecting"; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     socket = [[SRWebSocket alloc] initWithURLRequest:request securityPolicy:[OWSWebsocketSecurityPolicy sharedPolicy]]; | 
		
	
		
			
				|  |  |  |  |     socket.delegate = self; | 
		
	
		
			
				|  |  |  |  | // We need to keep websocket state and class state tightly aligned. | 
		
	
		
			
				|  |  |  |  | // | 
		
	
		
			
				|  |  |  |  | // Sometimes we'll need to update class state to reflect changes | 
		
	
		
			
				|  |  |  |  | // in socket state; sometimes we'll need to update socket state | 
		
	
		
			
				|  |  |  |  | // and class state to reflect changes in app state. | 
		
	
		
			
				|  |  |  |  | // | 
		
	
		
			
				|  |  |  |  | // We learn about changes to socket state through websocket | 
		
	
		
			
				|  |  |  |  | // delegate methods like [webSocketDidOpen:], [didFailWithError:...] | 
		
	
		
			
				|  |  |  |  | // and [didCloseWithCode:...].  These delegate methods are sometimes | 
		
	
		
			
				|  |  |  |  | // invoked _after_ web socket state changes, so we sometimes learn | 
		
	
		
			
				|  |  |  |  | // about changes to socket state in [ensureWebsocket].  Put another way, | 
		
	
		
			
				|  |  |  |  | // it's not safe to assume we'll learn of changes to websocket state | 
		
	
		
			
				|  |  |  |  | // in the websocket delegate methods. | 
		
	
		
			
				|  |  |  |  | // | 
		
	
		
			
				|  |  |  |  | // Therefore, we use the [setStatus:] setter to ensure alignment between | 
		
	
		
			
				|  |  |  |  | // websocket state and class state. | 
		
	
		
			
				|  |  |  |  | - (void)setStatus:(SocketStatus)status { | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     // If this status update is redundant, verify that | 
		
	
		
			
				|  |  |  |  |     // class state and socket state are aligned. | 
		
	
		
			
				|  |  |  |  |     if (_status == status) { | 
		
	
		
			
				|  |  |  |  |         switch (status) { | 
		
	
		
			
				|  |  |  |  |             case kSocketStatusClosed: | 
		
	
		
			
				|  |  |  |  |                 OWSAssert(!self.websocket); | 
		
	
		
			
				|  |  |  |  |                 break; | 
		
	
		
			
				|  |  |  |  |             case kSocketStatusOpen: | 
		
	
		
			
				|  |  |  |  |                 OWSAssert(self.websocket); | 
		
	
		
			
				|  |  |  |  |                 OWSAssert([self.websocket readyState] == SR_OPEN); | 
		
	
		
			
				|  |  |  |  |                 break; | 
		
	
		
			
				|  |  |  |  |             case kSocketStatusConnecting: | 
		
	
		
			
				|  |  |  |  |                 OWSAssert(self.websocket); | 
		
	
		
			
				|  |  |  |  |                 OWSAssert([self.websocket readyState] == SR_CONNECTING); | 
		
	
		
			
				|  |  |  |  |                 break; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         return; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     DDLogWarn(@"%@ Socket status change: %@ -> %@", | 
		
	
		
			
				|  |  |  |  |               self.tag, | 
		
	
		
			
				|  |  |  |  |               [self stringFromSocketStatus:_status], | 
		
	
		
			
				|  |  |  |  |               [self stringFromSocketStatus:status]); | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     // If this status update is _not_ redundant, | 
		
	
		
			
				|  |  |  |  |     // update class state to reflect the new status. | 
		
	
		
			
				|  |  |  |  |     switch (status) { | 
		
	
		
			
				|  |  |  |  |         case kSocketStatusClosed: { | 
		
	
		
			
				|  |  |  |  |             [self resetSocket]; | 
		
	
		
			
				|  |  |  |  |             break; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         case kSocketStatusOpen: { | 
		
	
		
			
				|  |  |  |  |             OWSAssert(self.status == kSocketStatusConnecting); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |             self.pingTimer = [NSTimer timerWithTimeInterval:kWebSocketHeartBeat | 
		
	
		
			
				|  |  |  |  |                                                      target:self | 
		
	
		
			
				|  |  |  |  |                                                    selector:@selector(webSocketHeartBeat) | 
		
	
		
			
				|  |  |  |  |                                                    userInfo:nil | 
		
	
		
			
				|  |  |  |  |                                                     repeats:YES]; | 
		
	
		
			
				|  |  |  |  |              | 
		
	
		
			
				|  |  |  |  |             // Additionally, we want the ping timer to work in the background too. | 
		
	
		
			
				|  |  |  |  |             [[NSRunLoop mainRunLoop] addTimer:self.pingTimer forMode:NSDefaultRunLoopMode]; | 
		
	
		
			
				|  |  |  |  |              | 
		
	
		
			
				|  |  |  |  |             [self.reconnectTimer invalidate]; | 
		
	
		
			
				|  |  |  |  |             self.reconnectTimer = nil; | 
		
	
		
			
				|  |  |  |  |             self.didConnectBg   = YES; | 
		
	
		
			
				|  |  |  |  |             break; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |         case kSocketStatusConnecting: { | 
		
	
		
			
				|  |  |  |  |             // Discard the old socket which is already closed or is closing. | 
		
	
		
			
				|  |  |  |  |             [self resetSocket]; | 
		
	
		
			
				|  |  |  |  |              | 
		
	
		
			
				|  |  |  |  |             // Create a new web socket. | 
		
	
		
			
				|  |  |  |  |             NSString *webSocketConnect = [textSecureWebSocketAPI stringByAppendingString:[self webSocketAuthenticationString]]; | 
		
	
		
			
				|  |  |  |  |             NSURL *webSocketConnectURL   = [NSURL URLWithString:webSocketConnect]; | 
		
	
		
			
				|  |  |  |  |             NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:webSocketConnectURL]; | 
		
	
		
			
				|  |  |  |  |              | 
		
	
		
			
				|  |  |  |  |             SRWebSocket *socket = [[SRWebSocket alloc] initWithURLRequest:request | 
		
	
		
			
				|  |  |  |  |                                                            securityPolicy:[OWSWebsocketSecurityPolicy sharedPolicy]]; | 
		
	
		
			
				|  |  |  |  |             socket.delegate = self; | 
		
	
		
			
				|  |  |  |  |              | 
		
	
		
			
				|  |  |  |  |             [self setWebsocket:socket]; | 
		
	
		
			
				|  |  |  |  |             [socket open]; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     [self setWebsocket:socket]; | 
		
	
		
			
				|  |  |  |  |     [socket open]; | 
		
	
		
			
				|  |  |  |  |     _status = status; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | - (void)resetSocket { | 
		
	
		
			
				|  |  |  |  |     self.websocket.delegate = nil; | 
		
	
		
			
				|  |  |  |  |     [self.websocket close]; | 
		
	
		
			
				|  |  |  |  |     self.websocket = nil; | 
		
	
		
			
				|  |  |  |  |     [self.pingTimer invalidate]; | 
		
	
		
			
				|  |  |  |  |     self.pingTimer = nil; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | + (void)resignActivity { | 
		
	
	
		
			
				
					|  |  |  | @ -159,11 +251,6 @@ NSString *const SocketConnectingNotification = @"SocketConnectingNotification"; | 
		
	
		
			
				|  |  |  |  |         DDLogWarn(@"%@ closeWebSocket.", self.tag); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     [self.websocket close]; | 
		
	
		
			
				|  |  |  |  |     self.websocket.delegate = nil; | 
		
	
		
			
				|  |  |  |  |     self.websocket = nil; | 
		
	
		
			
				|  |  |  |  |     [self.pingTimer invalidate]; | 
		
	
		
			
				|  |  |  |  |     self.pingTimer = nil; | 
		
	
		
			
				|  |  |  |  |     self.status = kSocketStatusClosed; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -172,20 +259,7 @@ NSString *const SocketConnectingNotification = @"SocketConnectingNotification"; | 
		
	
		
			
				|  |  |  |  | - (void)webSocketDidOpen:(SRWebSocket *)webSocket { | 
		
	
		
			
				|  |  |  |  |     OWSAssert([NSThread isMainThread]); | 
		
	
		
			
				|  |  |  |  |      | 
		
	
		
			
				|  |  |  |  |     self.pingTimer = [NSTimer timerWithTimeInterval:kWebSocketHeartBeat | 
		
	
		
			
				|  |  |  |  |                                              target:self | 
		
	
		
			
				|  |  |  |  |                                            selector:@selector(webSocketHeartBeat) | 
		
	
		
			
				|  |  |  |  |                                            userInfo:nil | 
		
	
		
			
				|  |  |  |  |                                             repeats:YES]; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Additionally, we want the ping timer to work in the background too. | 
		
	
		
			
				|  |  |  |  |     [[NSRunLoop mainRunLoop] addTimer:self.pingTimer forMode:NSDefaultRunLoopMode]; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     OWSAssert(self.status == kSocketStatusConnecting); | 
		
	
		
			
				|  |  |  |  |     self.status = kSocketStatusOpen; | 
		
	
		
			
				|  |  |  |  |     [self.reconnectTimer invalidate]; | 
		
	
		
			
				|  |  |  |  |     self.reconnectTimer = nil; | 
		
	
		
			
				|  |  |  |  |     self.didConnectBg   = YES; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { | 
		
	
	
		
			
				
					|  |  |  | 
 |