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 "ARDAppEngineClient.h"
12
13#import <WebRTC/RTCLogging.h>
14
15#import "ARDJoinResponse.h"
16#import "ARDMessageResponse.h"
17#import "ARDSignalingMessage.h"
18#import "ARDUtilities.h"
19
20// TODO(tkchin): move these to a configuration object.
21static NSString * const kARDRoomServerHostUrl =
22    @"https://appr.tc";
23static NSString * const kARDRoomServerJoinFormat =
24    @"https://appr.tc/join/%@";
25static NSString * const kARDRoomServerJoinFormatLoopback =
26    @"https://appr.tc/join/%@?debug=loopback";
27static NSString * const kARDRoomServerMessageFormat =
28    @"https://appr.tc/message/%@/%@";
29static NSString * const kARDRoomServerLeaveFormat =
30    @"https://appr.tc/leave/%@/%@";
31
32static NSString * const kARDAppEngineClientErrorDomain = @"ARDAppEngineClient";
33static NSInteger const kARDAppEngineClientErrorBadResponse = -1;
34
35@implementation ARDAppEngineClient
36
37#pragma mark - ARDRoomServerClient
38
39- (void)joinRoomWithRoomId:(NSString *)roomId
40                isLoopback:(BOOL)isLoopback
41         completionHandler:(void (^)(ARDJoinResponse *response,
42                                     NSError *error))completionHandler {
43  NSParameterAssert(roomId.length);
44
45  NSString *urlString = nil;
46  if (isLoopback) {
47    urlString =
48        [NSString stringWithFormat:kARDRoomServerJoinFormatLoopback, roomId];
49  } else {
50    urlString =
51        [NSString stringWithFormat:kARDRoomServerJoinFormat, roomId];
52  }
53
54  NSURL *roomURL = [NSURL URLWithString:urlString];
55  RTCLog(@"Joining room:%@ on room server.", roomId);
56  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:roomURL];
57  request.HTTPMethod = @"POST";
58  [NSURLConnection sendAsyncRequest:request
59                  completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
60                    if (error) {
61                      if (completionHandler) {
62                        completionHandler(nil, error);
63                      }
64                      return;
65                    }
66                    ARDJoinResponse *joinResponse = [ARDJoinResponse responseFromJSONData:data];
67                    if (!joinResponse) {
68                      if (completionHandler) {
69                        NSError *error = [[self class] badResponseError];
70                        completionHandler(nil, error);
71                      }
72                      return;
73                    }
74                    if (completionHandler) {
75                      completionHandler(joinResponse, nil);
76                    }
77                  }];
78}
79
80- (void)sendMessage:(ARDSignalingMessage *)message
81            forRoomId:(NSString *)roomId
82             clientId:(NSString *)clientId
83    completionHandler:(void (^)(ARDMessageResponse *response,
84                                NSError *error))completionHandler {
85  NSParameterAssert(message);
86  NSParameterAssert(roomId.length);
87  NSParameterAssert(clientId.length);
88
89  NSData *data = [message JSONData];
90  NSString *urlString =
91      [NSString stringWithFormat:
92          kARDRoomServerMessageFormat, roomId, clientId];
93  NSURL *url = [NSURL URLWithString:urlString];
94  RTCLog(@"C->RS POST: %@", message);
95  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
96  request.HTTPMethod = @"POST";
97  request.HTTPBody = data;
98  [NSURLConnection sendAsyncRequest:request
99                  completionHandler:^(NSURLResponse *response,
100                                      NSData *data,
101                                      NSError *error) {
102    if (error) {
103      if (completionHandler) {
104        completionHandler(nil, error);
105      }
106      return;
107    }
108    ARDMessageResponse *messageResponse =
109        [ARDMessageResponse responseFromJSONData:data];
110    if (!messageResponse) {
111      if (completionHandler) {
112        NSError *error = [[self class] badResponseError];
113        completionHandler(nil, error);
114      }
115      return;
116    }
117    if (completionHandler) {
118      completionHandler(messageResponse, nil);
119    }
120  }];
121}
122
123- (void)leaveRoomWithRoomId:(NSString *)roomId
124                   clientId:(NSString *)clientId
125          completionHandler:(void (^)(NSError *error))completionHandler {
126  NSParameterAssert(roomId.length);
127  NSParameterAssert(clientId.length);
128
129  NSString *urlString =
130      [NSString stringWithFormat:kARDRoomServerLeaveFormat, roomId, clientId];
131  NSURL *url = [NSURL URLWithString:urlString];
132  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
133  request.HTTPMethod = @"POST";
134
135  RTCLog(@"C->RS: BYE");
136  __block NSError *error = nil;
137
138  // We want a synchronous request so that we know that we've left the room on
139  // room server before we do any further work.
140  dispatch_semaphore_t sem = dispatch_semaphore_create(0);
141  [NSURLConnection sendAsyncRequest:request
142                  completionHandler:^(NSURLResponse *response, NSData *data, NSError *e) {
143                    if (e) {
144                      error = e;
145                    }
146                    dispatch_semaphore_signal(sem);
147                  }];
148
149  dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
150  if (error) {
151    RTCLogError(@"Error leaving room %@ on room server: %@", roomId, error.localizedDescription);
152    if (completionHandler) {
153      completionHandler(error);
154    }
155    return;
156  }
157  RTCLog(@"Left room:%@ on room server.", roomId);
158  if (completionHandler) {
159    completionHandler(nil);
160  }
161}
162
163#pragma mark - Private
164
165+ (NSError *)badResponseError {
166  NSError *error =
167      [[NSError alloc] initWithDomain:kARDAppEngineClientErrorDomain
168                                 code:kARDAppEngineClientErrorBadResponse
169                             userInfo:@{
170    NSLocalizedDescriptionKey: @"Error parsing response.",
171  }];
172  return error;
173}
174
175@end
176