Home | History | Annotate | Download | only in AppRTCDemo
      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.
     20 static NSString const *kARDWSSMessageErrorKey = @"error";
     21 static 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