• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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