зеркало из https://github.com/nextcloud/talk-ios.git
689 строки
24 KiB
Objective-C
689 строки
24 KiB
Objective-C
/**
|
|
* @copyright Copyright (c) 2020 Ivan Sein <ivan@nextcloud.com>
|
|
*
|
|
* @author Ivan Sein <ivan@nextcloud.com>
|
|
*
|
|
* @license GNU GPL version 3 or any later version
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#import "NCExternalSignalingController.h"
|
|
|
|
#import "NCAPIController.h"
|
|
#import "NCDatabaseManager.h"
|
|
#import "NCRoomsManager.h"
|
|
#import "NCSettingsController.h"
|
|
#import "WSMessage.h"
|
|
|
|
#import "NextcloudTalk-Swift.h"
|
|
|
|
static NSTimeInterval kInitialReconnectInterval = 1;
|
|
static NSTimeInterval kMaxReconnectInterval = 16;
|
|
static NSTimeInterval kWebSocketTimeoutInterval = 15;
|
|
|
|
@interface NCExternalSignalingController () <NSURLSessionWebSocketDelegate>
|
|
|
|
@property (nonatomic, strong) NSURLSessionWebSocketTask *webSocket;
|
|
@property (nonatomic, strong) NSString* serverUrl;
|
|
@property (nonatomic, strong) NSString* ticket;
|
|
@property (nonatomic, strong) NSString* resumeId;
|
|
@property (nonatomic, strong) NSString* sessionId;
|
|
@property (nonatomic, strong) NSString* userId;
|
|
@property (nonatomic, strong) NSString* authenticationBackendUrl;
|
|
@property (nonatomic, assign) BOOL helloResponseReceived;
|
|
@property (nonatomic, assign) BOOL mcuSupport;
|
|
@property (nonatomic, strong) NSMutableDictionary* participantsMap;
|
|
@property (nonatomic, strong) NSMutableArray* pendingMessages;
|
|
@property (nonatomic, assign) NSInteger messageId;
|
|
@property (nonatomic, strong) WSMessage *helloMessage;
|
|
@property (nonatomic, strong) NSMutableArray *messagesWithCompletionBlocks;
|
|
@property (nonatomic, assign) NSInteger reconnectInterval;
|
|
@property (nonatomic, strong) NSTimer *reconnectTimer;
|
|
@property (nonatomic, assign) BOOL sessionChanged;
|
|
|
|
@end
|
|
|
|
@implementation NCExternalSignalingController
|
|
|
|
- (instancetype)initWithAccount:(TalkAccount *)account server:(NSString *)serverUrl andTicket:(NSString *)ticket
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
_account = account;
|
|
_userId = _account.userId;
|
|
_authenticationBackendUrl = [[NCAPIController sharedInstance] authenticationBackendUrlForAccount:_account];
|
|
[self setServer:serverUrl andTicket:ticket];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)isEnabled
|
|
{
|
|
return (_serverUrl) ? YES : NO;
|
|
}
|
|
|
|
- (BOOL)hasMCU
|
|
{
|
|
return _mcuSupport;
|
|
}
|
|
|
|
- (NSString *)sessionId
|
|
{
|
|
return _sessionId;
|
|
}
|
|
|
|
- (void)setServer:(NSString *)serverUrl andTicket:(NSString *)ticket
|
|
{
|
|
_serverUrl = [self getWebSocketUrlForServer:serverUrl];
|
|
_ticket = ticket;
|
|
_reconnectInterval = kInitialReconnectInterval;
|
|
_pendingMessages = [NSMutableArray new];
|
|
|
|
[self connect];
|
|
}
|
|
|
|
- (NSString *)getWebSocketUrlForServer:(NSString *)serverUrl
|
|
{
|
|
NSString *wsUrl = [serverUrl copy];
|
|
|
|
// Change to websocket protocol
|
|
wsUrl = [wsUrl stringByReplacingOccurrencesOfString:@"https://" withString:@"wss://"];
|
|
wsUrl = [wsUrl stringByReplacingOccurrencesOfString:@"http://" withString:@"ws://"];
|
|
// Remove trailing slash
|
|
if([wsUrl hasSuffix:@"/"]) {
|
|
wsUrl = [wsUrl substringToIndex:[wsUrl length] - 1];
|
|
}
|
|
// Add spreed endpoint
|
|
wsUrl = [wsUrl stringByAppendingString:@"/spreed"];
|
|
|
|
return wsUrl;
|
|
}
|
|
|
|
#pragma mark - WebSocket connection
|
|
|
|
- (void)connect
|
|
{
|
|
// Do not try to connect if the app is running in the background
|
|
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground) {
|
|
[NCUtils log:@"Trying to create websocket connection while app is in the background"];
|
|
_disconnected = YES;
|
|
return;
|
|
}
|
|
|
|
[self invalidateReconnectionTimer];
|
|
_disconnected = NO;
|
|
_messageId = 1;
|
|
_messagesWithCompletionBlocks = [NSMutableArray new];
|
|
_helloResponseReceived = NO;
|
|
[NCUtils log:[NSString stringWithFormat:@"Connecting to: %@", _serverUrl]];
|
|
NSURL *url = [NSURL URLWithString:_serverUrl];
|
|
NSURLSession *wsSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
|
|
NSURLRequest *wsRequest = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:kWebSocketTimeoutInterval];
|
|
NSURLSessionWebSocketTask *webSocket = [wsSession webSocketTaskWithRequest:wsRequest];
|
|
|
|
_webSocket = webSocket;
|
|
|
|
[_webSocket resume];
|
|
|
|
[self receiveMessage];
|
|
}
|
|
|
|
- (void)reconnect
|
|
{
|
|
// Note: Make sure to call reconnect only from the main-thread!
|
|
if (_reconnectTimer) {
|
|
return;
|
|
}
|
|
|
|
[NCUtils log:[NSString stringWithFormat:@"Reconnecting to: %@", _serverUrl]];
|
|
|
|
[self resetWebSocket];
|
|
|
|
// Execute completion blocks on all messages
|
|
for (WSMessage *message in self->_messagesWithCompletionBlocks) {
|
|
[message executeCompletionBlockWithStatus:SendMessageSocketError];
|
|
}
|
|
|
|
[self setReconnectionTimer];
|
|
}
|
|
|
|
- (void)forceReconnect
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
self->_resumeId = nil;
|
|
[self reconnect];
|
|
});
|
|
}
|
|
|
|
- (void)disconnect
|
|
{
|
|
[NCUtils log:[NSString stringWithFormat:@"Disconnecting from: %@", _serverUrl]];
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self invalidateReconnectionTimer];
|
|
[self resetWebSocket];
|
|
});
|
|
}
|
|
|
|
- (void)resetWebSocket
|
|
{
|
|
[_webSocket cancel];
|
|
_webSocket = nil;
|
|
_helloResponseReceived = NO;
|
|
[_helloMessage ignoreCompletionBlock];
|
|
_helloMessage = nil;
|
|
_disconnected = YES;
|
|
}
|
|
|
|
- (void)setReconnectionTimer
|
|
{
|
|
[self invalidateReconnectionTimer];
|
|
// Wiggle interval a little bit to prevent all clients from connecting
|
|
// simultaneously in case the server connection is interrupted.
|
|
NSInteger interval = _reconnectInterval - (_reconnectInterval / 2) + arc4random_uniform((int)_reconnectInterval);
|
|
NSLog(@"Reconnecting in %ld", (long)interval);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
self->_reconnectTimer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(connect) userInfo:nil repeats:NO];
|
|
});
|
|
_reconnectInterval = _reconnectInterval * 2;
|
|
if (_reconnectInterval > kMaxReconnectInterval) {
|
|
_reconnectInterval = kMaxReconnectInterval;
|
|
}
|
|
}
|
|
|
|
- (void)invalidateReconnectionTimer
|
|
{
|
|
[_reconnectTimer invalidate];
|
|
_reconnectTimer = nil;
|
|
}
|
|
|
|
#pragma mark - WebSocket messages
|
|
|
|
- (void)sendMessage:(NSDictionary *)jsonDict withCompletionBlock:(SendMessageCompletionBlock)block
|
|
{
|
|
WSMessage *wsMessage = [[WSMessage alloc] initWithMessage:jsonDict withCompletionBlock:block];
|
|
|
|
// Add message as pending message if websocket is not connected
|
|
if (!_helloResponseReceived && !wsMessage.isHelloMessage) {
|
|
if (wsMessage.isJoinMessage) {
|
|
// We join a new room, so any message which wasn't send by now is not relevant for the new room anymore
|
|
_pendingMessages = [NSMutableArray new];
|
|
}
|
|
|
|
[_pendingMessages addObject:wsMessage];
|
|
return;
|
|
}
|
|
|
|
[self sendMessage:wsMessage];
|
|
}
|
|
|
|
- (void)sendMessage:(WSMessage *)wsMessage
|
|
{
|
|
// Assign messageId and timeout to messages with completionBlocks
|
|
if (wsMessage.completionBlock) {
|
|
NSString *messageIdString = [NSString stringWithFormat: @"%ld", (long)_messageId++];
|
|
wsMessage.messageId = messageIdString;
|
|
|
|
if (wsMessage.isHelloMessage) {
|
|
[_helloMessage ignoreCompletionBlock];
|
|
_helloMessage = wsMessage;
|
|
} else {
|
|
[_messagesWithCompletionBlocks addObject:wsMessage];
|
|
}
|
|
}
|
|
|
|
if (!wsMessage.webSocketMessage) {
|
|
NSLog(@"Error creating websocket message");
|
|
[wsMessage executeCompletionBlockWithStatus:SendMessageApplicationError];
|
|
return;
|
|
}
|
|
|
|
[wsMessage sendMessageWithWebSocket:_webSocket];
|
|
}
|
|
|
|
- (void)sendHelloMessage
|
|
{
|
|
NSDictionary *helloDict = @{
|
|
@"type": @"hello",
|
|
@"hello": @{
|
|
@"version": @"1.0",
|
|
@"auth": @{
|
|
@"url": _authenticationBackendUrl,
|
|
@"params": @{
|
|
@"userid": _userId,
|
|
@"ticket": _ticket
|
|
}
|
|
}
|
|
}
|
|
};
|
|
// Try to resume session
|
|
if (_resumeId) {
|
|
helloDict = @{
|
|
@"type": @"hello",
|
|
@"hello": @{
|
|
@"version": @"1.0",
|
|
@"resumeid": _resumeId
|
|
}
|
|
};
|
|
}
|
|
|
|
[self sendMessage:helloDict withCompletionBlock:^(NSURLSessionWebSocketTask *task, NCExternalSignalingSendMessageStatus status) {
|
|
if (status == SendMessageSocketError && task == self->_webSocket) {
|
|
[NCUtils log:[NSString stringWithFormat:@"Reconnecting from sendHelloMessage"]];
|
|
[self reconnect];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)helloResponseReceived:(NSDictionary *)messageDict
|
|
{
|
|
_helloResponseReceived = YES;
|
|
|
|
NSString *messageId = [messageDict objectForKey:@"id"];
|
|
[self executeCompletionBlockForMessageId:messageId withStatus:SendMessageSuccess];
|
|
|
|
NSDictionary *helloDict = [messageDict objectForKey:@"hello"];
|
|
_resumeId = [helloDict objectForKey:@"resumeid"];
|
|
NSString *newSessionId = [helloDict objectForKey:@"sessionid"];
|
|
_sessionChanged = _sessionId && ![_sessionId isEqualToString:newSessionId];
|
|
_sessionId = newSessionId;
|
|
NSArray *serverFeatures = [[helloDict objectForKey:@"server"] objectForKey:@"features"];
|
|
for (NSString *feature in serverFeatures) {
|
|
if ([feature isEqualToString:@"mcu"]) {
|
|
_mcuSupport = YES;
|
|
}
|
|
}
|
|
|
|
NSString *serverVersion = [[helloDict objectForKey:@"server"] objectForKey:@"version"];
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"NCUpdateSignalingVersionTransaction" expirationHandler:nil];
|
|
[[NCDatabaseManager sharedInstance] setExternalSignalingServerVersion:serverVersion forAccountId:self->_account.accountId];
|
|
[bgTask stopBackgroundTask];
|
|
});
|
|
|
|
// Send pending messages
|
|
for (WSMessage *wsMessage in _pendingMessages) {
|
|
[self sendMessage:wsMessage];
|
|
}
|
|
_pendingMessages = [NSMutableArray new];
|
|
|
|
// Re-join if user was in a room
|
|
if (_currentRoom && _sessionChanged) {
|
|
[self.delegate externalSignalingControllerWillRejoinCall:self];
|
|
[[NCRoomsManager sharedInstance] rejoinRoom:_currentRoom];
|
|
}
|
|
}
|
|
|
|
- (void)errorResponseReceived:(NSDictionary *)messageDict
|
|
{
|
|
NSString *errorCode = [[messageDict objectForKey:@"error"] objectForKey:@"code"];
|
|
|
|
[NCUtils log:[NSString stringWithFormat:@"Received error response %@", errorCode]];
|
|
|
|
if ([errorCode isEqualToString:@"no_such_session"]) {
|
|
// We could not resume the previous session, but the websocket is still alive -> resend the hello message without a resumeId
|
|
_resumeId = nil;
|
|
[self sendHelloMessage];
|
|
|
|
return;
|
|
}
|
|
|
|
NSString *messageId = [messageDict objectForKey:@"id"];
|
|
[self executeCompletionBlockForMessageId:messageId withStatus:SendMessageApplicationError];
|
|
}
|
|
|
|
- (void)joinRoom:(NSString *)roomId withSessionId:(NSString *)sessionId withCompletionBlock:(JoinRoomExternalSignalingCompletionBlock)block
|
|
{
|
|
|
|
if (_disconnected) {
|
|
[NCUtils log:[NSString stringWithFormat:@"Joining room %@, but the websocket is disconnected.", roomId]];
|
|
}
|
|
|
|
if (_webSocket == nil) {
|
|
[NCUtils log:[NSString stringWithFormat:@"Joining room %@, but the websocket is nil.", roomId]];
|
|
}
|
|
|
|
NSDictionary *messageDict = @{
|
|
@"type": @"room",
|
|
@"room": @{
|
|
@"roomid": roomId,
|
|
@"sessionid": sessionId
|
|
}
|
|
};
|
|
|
|
[self sendMessage:messageDict withCompletionBlock:^(NSURLSessionWebSocketTask *task, NCExternalSignalingSendMessageStatus status) {
|
|
if (status == SendMessageSocketError && task == self->_webSocket) {
|
|
// Reconnect if this is still the same socket we tried to send the message on
|
|
[NCUtils log:[NSString stringWithFormat:@"Reconnect from joinRoom"]];
|
|
[self reconnect];
|
|
}
|
|
|
|
if (block) {
|
|
NSError *error = nil;
|
|
|
|
if (status != SendMessageSuccess) {
|
|
error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:nil];
|
|
}
|
|
|
|
block(error);
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)leaveRoom:(NSString *)roomId
|
|
{
|
|
if ([_currentRoom isEqualToString:roomId]) {
|
|
_currentRoom = nil;
|
|
[self joinRoom:@"" withSessionId:@"" withCompletionBlock:nil];
|
|
} else {
|
|
NSLog(@"External signaling: Not leaving because it's not room we joined");
|
|
}
|
|
}
|
|
|
|
- (void)sendCallMessage:(NCSignalingMessage *)message
|
|
{
|
|
NSDictionary *messageDict = @{
|
|
@"type": @"message",
|
|
@"message": @{
|
|
@"recipient": @{
|
|
@"type": @"session",
|
|
@"sessionid": message.to
|
|
},
|
|
@"data": [message functionDict]
|
|
}
|
|
};
|
|
|
|
[self sendMessage:messageDict withCompletionBlock:nil];
|
|
}
|
|
|
|
- (void)requestOfferForSessionId:(NSString *)sessionId andRoomType:(NSString *)roomType
|
|
{
|
|
NSDictionary *messageDict = @{
|
|
@"type": @"message",
|
|
@"message": @{
|
|
@"recipient": @{
|
|
@"type": @"session",
|
|
@"sessionid": sessionId
|
|
},
|
|
@"data": @{
|
|
@"type": @"requestoffer",
|
|
@"roomType": roomType
|
|
}
|
|
}
|
|
};
|
|
|
|
[self sendMessage:messageDict withCompletionBlock:nil];
|
|
}
|
|
|
|
- (void)roomMessageReceived:(NSDictionary *)messageDict
|
|
{
|
|
_participantsMap = [NSMutableDictionary new];
|
|
_currentRoom = [[messageDict objectForKey:@"room"] objectForKey:@"roomid"];
|
|
|
|
NSString *messageId = [messageDict objectForKey:@"id"];
|
|
[self executeCompletionBlockForMessageId:messageId withStatus:SendMessageSuccess];
|
|
|
|
// Notify that session has change to rejoin the call if currently in a call
|
|
if (_sessionChanged) {
|
|
_sessionChanged = NO;
|
|
[self.delegate externalSignalingControllerShouldRejoinCall:self];
|
|
}
|
|
}
|
|
|
|
- (void)eventMessageReceived:(NSDictionary *)eventDict
|
|
{
|
|
NSString *eventTarget = [eventDict objectForKey:@"target"];
|
|
if ([eventTarget isEqualToString:@"room"]) {
|
|
[self processRoomEvent:eventDict];
|
|
} else if ([eventTarget isEqualToString:@"roomlist"]) {
|
|
[self processRoomListEvent:eventDict];
|
|
} else if ([eventTarget isEqualToString:@"participants"]) {
|
|
[self processRoomParticipantsEvent:eventDict];
|
|
} else {
|
|
NSLog(@"Unsupported event target: %@", eventDict);
|
|
}
|
|
}
|
|
|
|
- (void)processRoomEvent:(NSDictionary *)eventDict
|
|
{
|
|
NSString *eventType = [eventDict objectForKey:@"type"];
|
|
if ([eventType isEqualToString:@"join"]) {
|
|
NSArray *joins = [eventDict objectForKey:@"join"];
|
|
for (NSDictionary *participant in joins) {
|
|
NSString *participantId = [participant objectForKey:@"userid"];
|
|
if (!participantId || [participantId isEqualToString:@""]) {
|
|
NSLog(@"Guest joined room.");
|
|
} else {
|
|
if ([participantId isEqualToString:_userId]) {
|
|
NSLog(@"App user joined room.");
|
|
} else {
|
|
NSLog(@"Participant joined room.");
|
|
}
|
|
[_participantsMap setObject:participant forKey:[participant objectForKey:@"sessionid"]];
|
|
}
|
|
}
|
|
} else if ([eventType isEqualToString:@"leave"]) {
|
|
NSLog(@"Participant left room.");
|
|
} else if ([eventType isEqualToString:@"message"]) {
|
|
[self processRoomMessageEvent:[eventDict objectForKey:@"message"]];
|
|
} else {
|
|
NSLog(@"Unknown room event: %@", eventDict);
|
|
}
|
|
}
|
|
|
|
- (void)processRoomMessageEvent:(NSDictionary *)messageDict
|
|
{
|
|
NSString *messageType = [[messageDict objectForKey:@"data"] objectForKey:@"type"];
|
|
if ([messageType isEqualToString:@"chat"]) {
|
|
NSLog(@"Chat message received.");
|
|
} else {
|
|
NSLog(@"Unknown room message type: %@", messageDict);
|
|
}
|
|
}
|
|
|
|
- (void)processRoomListEvent:(NSDictionary *)eventDict
|
|
{
|
|
NSLog(@"Refresh room list.");
|
|
}
|
|
|
|
- (void)processRoomParticipantsEvent:(NSDictionary *)eventDict
|
|
{
|
|
NSString *eventType = [eventDict objectForKey:@"type"];
|
|
if ([eventType isEqualToString:@"update"]) {
|
|
NSLog(@"Participant list changed: %@", [eventDict objectForKey:@"update"]);
|
|
[self.delegate externalSignalingController:self didReceivedParticipantListMessage:[eventDict objectForKey:@"update"]];
|
|
} else {
|
|
NSLog(@"Unknown room event: %@", eventDict);
|
|
}
|
|
}
|
|
|
|
- (void)messageReceived:(NSDictionary *)messageDict
|
|
{
|
|
NSLog(@"Message received");
|
|
[self.delegate externalSignalingController:self didReceivedSignalingMessage:messageDict];
|
|
}
|
|
|
|
#pragma mark - Completion blocks
|
|
|
|
- (void)executeCompletionBlockForMessageId:(NSString *)messageId withStatus:(NCExternalSignalingSendMessageStatus)status
|
|
{
|
|
if (!messageId) {
|
|
return;
|
|
}
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if ([self->_helloMessage.messageId isEqualToString:messageId]) {
|
|
[self->_helloMessage executeCompletionBlockWithStatus:status];
|
|
self->_helloMessage = nil;
|
|
return;
|
|
}
|
|
|
|
for (WSMessage *message in self->_messagesWithCompletionBlocks) {
|
|
if ([messageId isEqualToString:message.messageId]) {
|
|
[message executeCompletionBlockWithStatus:status];
|
|
[self->_messagesWithCompletionBlocks removeObject:message];
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
#pragma mark - NSURLSessionWebSocketDelegate
|
|
|
|
- (void)URLSession:(NSURLSession *)session webSocketTask:(NSURLSessionWebSocketTask *)webSocketTask didOpenWithProtocol:(NSString *)protocol
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (webSocketTask != self->_webSocket) {
|
|
return;
|
|
}
|
|
|
|
|
|
NSLog(@"WebSocket Connected!");
|
|
self->_reconnectInterval = kInitialReconnectInterval;
|
|
[self sendHelloMessage];
|
|
});
|
|
}
|
|
|
|
- (void)URLSession:(NSURLSession *)session webSocketTask:(NSURLSessionWebSocketTask *)webSocketTask didCloseWithCode:(NSURLSessionWebSocketCloseCode)closeCode reason:(NSData *)reason
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (webSocketTask != self->_webSocket) {
|
|
return;
|
|
}
|
|
|
|
NSLog(@"WebSocket didCloseWithCode:%ld reason:%@", (long)closeCode, reason);
|
|
[self reconnect];
|
|
});
|
|
}
|
|
|
|
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (task != self->_webSocket) {
|
|
return;
|
|
}
|
|
|
|
if (error) {
|
|
NSLog(@"WebSocket session didCompleteWithError: %@", error.description);
|
|
[self reconnect];
|
|
}
|
|
});
|
|
}
|
|
|
|
- (void)receiveMessage {
|
|
__weak NCExternalSignalingController *weakSelf = self;
|
|
__block NSURLSessionWebSocketTask *receivingWebSocket = _webSocket;
|
|
|
|
[_webSocket receiveMessageWithCompletionHandler:^(NSURLSessionWebSocketMessage * _Nullable message, NSError * _Nullable error) {
|
|
if (!error) {
|
|
NSData *messageData = message.data;
|
|
NSString *messageString = message.string;
|
|
|
|
if (message.type == NSURLSessionWebSocketMessageTypeString) {
|
|
messageData = [message.string dataUsingEncoding:NSUTF8StringEncoding];
|
|
}
|
|
|
|
if (message.type == NSURLSessionWebSocketMessageTypeData) {
|
|
messageString = [[NSString alloc] initWithData:messageData encoding:NSUTF8StringEncoding];
|
|
}
|
|
|
|
NSLog(@"WebSocket didReceiveMessage: %@", messageString);
|
|
NSDictionary *messageDict = [weakSelf getWebSocketMessageFromJSONData:messageData];
|
|
NSString *messageType = [messageDict objectForKey:@"type"];
|
|
if ([messageType isEqualToString:@"hello"]) {
|
|
[weakSelf helloResponseReceived:messageDict];
|
|
} else if ([messageType isEqualToString:@"error"]) {
|
|
[weakSelf errorResponseReceived:messageDict];
|
|
} else if ([messageType isEqualToString:@"room"]) {
|
|
[weakSelf roomMessageReceived:messageDict];
|
|
} else if ([messageType isEqualToString:@"event"]) {
|
|
[weakSelf eventMessageReceived:[messageDict objectForKey:@"event"]];
|
|
} else if ([messageType isEqualToString:@"message"]) {
|
|
[weakSelf messageReceived:[messageDict objectForKey:@"message"]];
|
|
} else if ([messageType isEqualToString:@"control"]) {
|
|
[weakSelf messageReceived:[messageDict objectForKey:@"control"]];
|
|
}
|
|
|
|
// Completion block for messageId should have been handled already at this point
|
|
NSString *messageId = [messageDict objectForKey:@"id"];
|
|
[weakSelf executeCompletionBlockForMessageId:messageId withStatus:SendMessageApplicationError];
|
|
|
|
[weakSelf receiveMessage];
|
|
} else {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
// Only try to reconnect if the webSocket is still the one we tried to receive a message on
|
|
if (receivingWebSocket != weakSelf.webSocket) {
|
|
return;
|
|
}
|
|
|
|
NSLog(@"WebSocket receiveMessageWithCompletionHandler error %@", error.description);
|
|
[weakSelf reconnect];
|
|
});
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
|
|
{
|
|
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
|
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
|
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
|
|
} else {
|
|
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
|
|
}
|
|
}
|
|
|
|
#pragma mark - Utils
|
|
|
|
- (NSString *)getUserIdFromSessionId:(NSString *)sessionId
|
|
{
|
|
NSString *userId = nil;
|
|
NSDictionary *user = [_participantsMap objectForKey:sessionId];
|
|
if (user) {
|
|
userId = [user objectForKey:@"userid"];
|
|
}
|
|
return userId;
|
|
}
|
|
|
|
- (NSString *)getDisplayNameFromSessionId:(NSString *)sessionId
|
|
{
|
|
NSString *displayName = nil;
|
|
NSDictionary *user = [_participantsMap objectForKey:sessionId];
|
|
if (user) {
|
|
NSDictionary *userSubKey = [user objectForKey:@"user"];
|
|
|
|
if (userSubKey) {
|
|
displayName = [userSubKey objectForKey:@"displayname"];
|
|
}
|
|
}
|
|
return displayName;
|
|
}
|
|
|
|
- (NSDictionary *)getWebSocketMessageFromJSONData:(NSData *)jsonData
|
|
{
|
|
NSError *error;
|
|
NSDictionary* messageDict = [NSJSONSerialization JSONObjectWithData:jsonData
|
|
options:kNilOptions
|
|
error:&error];
|
|
if (!messageDict) {
|
|
NSLog(@"Error parsing websocket message: %@", error);
|
|
}
|
|
|
|
return messageDict;
|
|
}
|
|
|
|
@end
|