• 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 "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      RTCSessionDescription *description = sdpMessage.sessionDescription;
221      NSString *dsc = description.description;
222      dsc = [dsc stringByReplacingOccurrencesOfString:@"offer"
223                                           withString:@"answer"];
224      RTCSessionDescription *answerDescription =
225          [[RTCSessionDescription alloc] initWithType:@"answer" 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      // Send back to server.
237      [self sendMessage:message];
238      break;
239    case kARDSignalingMessageTypeBye:
240      // Nothing to do.
241      return;
242  }
243}
244
245- (void)channel:(id<ARDSignalingChannel>)channel
246    didChangeState:(ARDSignalingChannelState)state {
247}
248
249@end
250
251