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