1/* 2 * Copyright 2014 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#import "ARDWebSocketChannel.h" 12 13#import "sdk/objc/base/RTCLogging.h" 14 15#import "SRWebSocket.h" 16 17#import "ARDSignalingMessage.h" 18#import "ARDUtilities.h" 19 20// TODO(tkchin): move these to a configuration object. 21static NSString const *kARDWSSMessageErrorKey = @"error"; 22static NSString const *kARDWSSMessagePayloadKey = @"msg"; 23 24@interface ARDWebSocketChannel () <SRWebSocketDelegate> 25@end 26 27@implementation ARDWebSocketChannel { 28 NSURL *_url; 29 NSURL *_restURL; 30 SRWebSocket *_socket; 31} 32 33@synthesize delegate = _delegate; 34@synthesize state = _state; 35@synthesize roomId = _roomId; 36@synthesize clientId = _clientId; 37 38- (instancetype)initWithURL:(NSURL *)url 39 restURL:(NSURL *)restURL 40 delegate:(id<ARDSignalingChannelDelegate>)delegate { 41 if (self = [super init]) { 42 _url = url; 43 _restURL = restURL; 44 _delegate = delegate; 45 _socket = [[SRWebSocket alloc] initWithURL:url]; 46 _socket.delegate = self; 47 RTCLog(@"Opening WebSocket."); 48 [_socket open]; 49 } 50 return self; 51} 52 53- (void)dealloc { 54 [self disconnect]; 55} 56 57- (void)setState:(ARDSignalingChannelState)state { 58 if (_state == state) { 59 return; 60 } 61 _state = state; 62 [_delegate channel:self didChangeState:_state]; 63} 64 65- (void)registerForRoomId:(NSString *)roomId 66 clientId:(NSString *)clientId { 67 NSParameterAssert(roomId.length); 68 NSParameterAssert(clientId.length); 69 _roomId = roomId; 70 _clientId = clientId; 71 if (_state == kARDSignalingChannelStateOpen) { 72 [self registerWithCollider]; 73 } 74} 75 76- (void)sendMessage:(ARDSignalingMessage *)message { 77 NSParameterAssert(_clientId.length); 78 NSParameterAssert(_roomId.length); 79 NSData *data = [message JSONData]; 80 if (_state == kARDSignalingChannelStateRegistered) { 81 NSString *payload = 82 [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 83 NSDictionary *message = @{ 84 @"cmd": @"send", 85 @"msg": payload, 86 }; 87 NSData *messageJSONObject = 88 [NSJSONSerialization dataWithJSONObject:message 89 options:NSJSONWritingPrettyPrinted 90 error:nil]; 91 NSString *messageString = 92 [[NSString alloc] initWithData:messageJSONObject 93 encoding:NSUTF8StringEncoding]; 94 RTCLog(@"C->WSS: %@", messageString); 95 [_socket send:messageString]; 96 } else { 97 NSString *dataString = 98 [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 99 RTCLog(@"C->WSS POST: %@", dataString); 100 NSString *urlString = 101 [NSString stringWithFormat:@"%@/%@/%@", 102 [_restURL absoluteString], _roomId, _clientId]; 103 NSURL *url = [NSURL URLWithString:urlString]; 104 [NSURLConnection sendAsyncPostToURL:url 105 withData:data 106 completionHandler:nil]; 107 } 108} 109 110- (void)disconnect { 111 if (_state == kARDSignalingChannelStateClosed || 112 _state == kARDSignalingChannelStateError) { 113 return; 114 } 115 [_socket close]; 116 RTCLog(@"C->WSS DELETE rid:%@ cid:%@", _roomId, _clientId); 117 NSString *urlString = 118 [NSString stringWithFormat:@"%@/%@/%@", 119 [_restURL absoluteString], _roomId, _clientId]; 120 NSURL *url = [NSURL URLWithString:urlString]; 121 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 122 request.HTTPMethod = @"DELETE"; 123 request.HTTPBody = nil; 124 [NSURLConnection sendAsyncRequest:request completionHandler:nil]; 125} 126 127#pragma mark - SRWebSocketDelegate 128 129- (void)webSocketDidOpen:(SRWebSocket *)webSocket { 130 RTCLog(@"WebSocket connection opened."); 131 self.state = kARDSignalingChannelStateOpen; 132 if (_roomId.length && _clientId.length) { 133 [self registerWithCollider]; 134 } 135} 136 137- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { 138 NSString *messageString = message; 139 NSData *messageData = [messageString dataUsingEncoding:NSUTF8StringEncoding]; 140 id jsonObject = [NSJSONSerialization JSONObjectWithData:messageData 141 options:0 142 error:nil]; 143 if (![jsonObject isKindOfClass:[NSDictionary class]]) { 144 RTCLogError(@"Unexpected message: %@", jsonObject); 145 return; 146 } 147 NSDictionary *wssMessage = jsonObject; 148 NSString *errorString = wssMessage[kARDWSSMessageErrorKey]; 149 if (errorString.length) { 150 RTCLogError(@"WSS error: %@", errorString); 151 return; 152 } 153 NSString *payload = wssMessage[kARDWSSMessagePayloadKey]; 154 ARDSignalingMessage *signalingMessage = 155 [ARDSignalingMessage messageFromJSONString:payload]; 156 RTCLog(@"WSS->C: %@", payload); 157 [_delegate channel:self didReceiveMessage:signalingMessage]; 158} 159 160- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { 161 RTCLogError(@"WebSocket error: %@", error); 162 self.state = kARDSignalingChannelStateError; 163} 164 165- (void)webSocket:(SRWebSocket *)webSocket 166 didCloseWithCode:(NSInteger)code 167 reason:(NSString *)reason 168 wasClean:(BOOL)wasClean { 169 RTCLog(@"WebSocket closed with code: %ld reason:%@ wasClean:%d", 170 (long)code, reason, wasClean); 171 NSParameterAssert(_state != kARDSignalingChannelStateError); 172 self.state = kARDSignalingChannelStateClosed; 173} 174 175#pragma mark - Private 176 177- (void)registerWithCollider { 178 if (_state == kARDSignalingChannelStateRegistered) { 179 return; 180 } 181 NSParameterAssert(_roomId.length); 182 NSParameterAssert(_clientId.length); 183 NSDictionary *registerMessage = @{ 184 @"cmd": @"register", 185 @"roomid" : _roomId, 186 @"clientid" : _clientId, 187 }; 188 NSData *message = 189 [NSJSONSerialization dataWithJSONObject:registerMessage 190 options:NSJSONWritingPrettyPrinted 191 error:nil]; 192 NSString *messageString = 193 [[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding]; 194 RTCLog(@"Registering on WSS for rid:%@ cid:%@", _roomId, _clientId); 195 // Registration can fail if server rejects it. For example, if the room is 196 // full. 197 [_socket send:messageString]; 198 self.state = kARDSignalingChannelStateRegistered; 199} 200 201@end 202 203@interface ARDLoopbackWebSocketChannel () <ARDSignalingChannelDelegate> 204@end 205 206@implementation ARDLoopbackWebSocketChannel 207 208- (instancetype)initWithURL:(NSURL *)url restURL:(NSURL *)restURL { 209 return [super initWithURL:url restURL:restURL delegate:self]; 210} 211 212#pragma mark - ARDSignalingChannelDelegate 213 214- (void)channel:(id<ARDSignalingChannel>)channel 215 didReceiveMessage:(ARDSignalingMessage *)message { 216 switch (message.type) { 217 case kARDSignalingMessageTypeOffer: { 218 // Change message to answer, send back to server. 219 ARDSessionDescriptionMessage *sdpMessage = 220 (ARDSessionDescriptionMessage *)message; 221 RTC_OBJC_TYPE(RTCSessionDescription) *description = sdpMessage.sessionDescription; 222 NSString *dsc = description.sdp; 223 dsc = [dsc stringByReplacingOccurrencesOfString:@"offer" 224 withString:@"answer"]; 225 RTC_OBJC_TYPE(RTCSessionDescription) *answerDescription = 226 [[RTC_OBJC_TYPE(RTCSessionDescription) alloc] initWithType:RTCSdpTypeAnswer sdp:dsc]; 227 ARDSignalingMessage *answer = 228 [[ARDSessionDescriptionMessage alloc] 229 initWithDescription:answerDescription]; 230 [self sendMessage:answer]; 231 break; 232 } 233 case kARDSignalingMessageTypeAnswer: 234 // Should not receive answer in loopback scenario. 235 break; 236 case kARDSignalingMessageTypeCandidate: 237 case kARDSignalingMessageTypeCandidateRemoval: 238 // Send back to server. 239 [self sendMessage:message]; 240 break; 241 case kARDSignalingMessageTypeBye: 242 // Nothing to do. 243 return; 244 } 245} 246 247- (void)channel:(id<ARDSignalingChannel>)channel 248 didChangeState:(ARDSignalingChannelState)state { 249} 250 251@end 252 253