ARDWebSocketChannel.m (8563B)
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. 21 static NSString const *kARDWSSMessageErrorKey = @"error"; 22 static 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 self = [super init]; 42 if (self) { 43 _url = url; 44 _restURL = restURL; 45 _delegate = delegate; 46 _socket = [[SRWebSocket alloc] initWithURL:url]; 47 _socket.delegate = self; 48 RTCLog(@"Opening WebSocket."); 49 [_socket open]; 50 } 51 return self; 52 } 53 54 - (void)dealloc { 55 [self disconnect]; 56 } 57 58 - (void)setState:(ARDSignalingChannelState)state { 59 if (_state == state) { 60 return; 61 } 62 _state = state; 63 [_delegate channel:self didChangeState:_state]; 64 } 65 66 - (void)registerForRoomId:(NSString *)roomId 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 = [[NSString alloc] initWithData:data 82 encoding:NSUTF8StringEncoding]; 83 NSData *messageJSONObject = [NSJSONSerialization 84 dataWithJSONObject:@{@"cmd" : @"send", @"msg" : payload} 85 options:NSJSONWritingPrettyPrinted 86 error:nil]; 87 NSString *messageString = 88 [[NSString alloc] initWithData:messageJSONObject 89 encoding:NSUTF8StringEncoding]; 90 RTCLog(@"C->WSS: %@", messageString); 91 [_socket send:messageString]; 92 } else { 93 NSString *dataString = [[NSString alloc] initWithData:data 94 encoding:NSUTF8StringEncoding]; 95 RTCLog(@"C->WSS POST: %@", dataString); 96 NSString *urlString = [NSString stringWithFormat:@"%@/%@/%@", 97 [_restURL absoluteString], 98 _roomId, 99 _clientId]; 100 NSURL *url = [NSURL URLWithString:urlString]; 101 [NSURLConnection sendAsyncPostToURL:url 102 withData:data 103 completionHandler:nil]; 104 } 105 } 106 107 - (void)disconnect { 108 if (_state == kARDSignalingChannelStateClosed || 109 _state == kARDSignalingChannelStateError) { 110 return; 111 } 112 [_socket close]; 113 RTCLog(@"C->WSS DELETE rid:%@ cid:%@", _roomId, _clientId); 114 NSString *urlString = [NSString stringWithFormat:@"%@/%@/%@", 115 [_restURL absoluteString], 116 _roomId, 117 _clientId]; 118 NSURL *url = [NSURL URLWithString:urlString]; 119 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 120 request.HTTPMethod = @"DELETE"; 121 request.HTTPBody = nil; 122 [NSURLConnection sendAsyncRequest:request completionHandler:nil]; 123 } 124 125 #pragma mark - SRWebSocketDelegate 126 127 - (void)webSocketDidOpen:(SRWebSocket *)webSocket { 128 RTCLog(@"WebSocket connection opened."); 129 self.state = kARDSignalingChannelStateOpen; 130 if (_roomId.length && _clientId.length) { 131 [self registerWithCollider]; 132 } 133 } 134 135 - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { 136 NSString *messageString = message; 137 NSData *messageData = [messageString dataUsingEncoding:NSUTF8StringEncoding]; 138 id jsonObject = [NSJSONSerialization JSONObjectWithData:messageData 139 options:0 140 error:nil]; 141 if (![jsonObject isKindOfClass:[NSDictionary class]]) { 142 RTCLogError(@"Unexpected message: %@", jsonObject); 143 return; 144 } 145 NSDictionary *wssMessage = jsonObject; 146 NSString *errorString = wssMessage[kARDWSSMessageErrorKey]; 147 if (errorString.length) { 148 RTCLogError(@"WSS error: %@", errorString); 149 return; 150 } 151 NSString *payload = wssMessage[kARDWSSMessagePayloadKey]; 152 ARDSignalingMessage *signalingMessage = 153 [ARDSignalingMessage messageFromJSONString:payload]; 154 RTCLog(@"WSS->C: %@", payload); 155 [_delegate channel:self didReceiveMessage:signalingMessage]; 156 } 157 158 - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { 159 RTCLogError(@"WebSocket error: %@", error); 160 self.state = kARDSignalingChannelStateError; 161 } 162 163 - (void)webSocket:(SRWebSocket *)webSocket 164 didCloseWithCode:(NSInteger)code 165 reason:(NSString *)reason 166 wasClean:(BOOL)wasClean { 167 RTCLog(@"WebSocket closed with code: %ld reason:%@ wasClean:%d", 168 (long)code, 169 reason, 170 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 = 222 sdpMessage.sessionDescription; 223 NSString *dsc = description.sdp; 224 dsc = [dsc stringByReplacingOccurrencesOfString:@"offer" 225 withString:@"answer"]; 226 RTC_OBJC_TYPE(RTCSessionDescription) *answerDescription = 227 [[RTC_OBJC_TYPE(RTCSessionDescription) alloc] 228 initWithType:RTCSdpTypeAnswer 229 sdp:dsc]; 230 ARDSignalingMessage *answer = [[ARDSessionDescriptionMessage alloc] 231 initWithDescription:answerDescription]; 232 [self sendMessage:answer]; 233 break; 234 } 235 case kARDSignalingMessageTypeAnswer: 236 // Should not receive answer in loopback scenario. 237 break; 238 case kARDSignalingMessageTypeCandidate: 239 case kARDSignalingMessageTypeCandidateRemoval: 240 // Send back to server. 241 [self sendMessage:message]; 242 break; 243 case kARDSignalingMessageTypeBye: 244 // Nothing to do. 245 return; 246 } 247 } 248 249 - (void)channel:(id<ARDSignalingChannel>)channel 250 didChangeState:(ARDSignalingChannelState)state { 251 } 252 253 @end