blob: 9ef0a7a74a1b250717c168852f75eeaf3add4612 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2013, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#import "APPRTCAppClient.h"
29
30#import <dispatch/dispatch.h>
31
32#import "GAEChannelClient.h"
fischman@webrtc.org1bc19542013-08-01 18:29:45 +000033#import "RTCICEServer.h"
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +000034#import "APPRTCAppDelegate.h"
35#import "RTCMediaConstraints.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000036
37@interface APPRTCAppClient ()
38
fischman@webrtc.orgd0f4c212013-08-20 22:16:55 +000039@property(nonatomic, strong) dispatch_queue_t backgroundQueue;
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000040@property(nonatomic, copy) NSString* baseURL;
41@property(nonatomic, strong) GAEChannelClient* gaeChannel;
42@property(nonatomic, copy) NSString* postMessageUrl;
43@property(nonatomic, copy) NSString* pcConfig;
44@property(nonatomic, strong) NSMutableString* roomHtml;
45@property(atomic, strong) NSMutableArray* sendQueue;
46@property(nonatomic, copy) NSString* token;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000047
48@property(nonatomic, assign) BOOL verboseLogging;
49
50@end
51
52@implementation APPRTCAppClient
53
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000054- (id)initWithICEServerDelegate:(id<ICEServerDelegate>)delegate
55 messageHandler:(id<GAEMessageHandler>)handler {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000056 if (self = [super init]) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000057 _ICEServerDelegate = delegate;
58 _messageHandler = handler;
fischman@webrtc.org7c82ada2014-04-30 00:17:47 +000059 _backgroundQueue = dispatch_queue_create("RTCBackgroundQueue",
60 DISPATCH_QUEUE_SERIAL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000061 _sendQueue = [NSMutableArray array];
62 // Uncomment to see Request/Response logging.
fischman@webrtc.org1bc19542013-08-01 18:29:45 +000063 // _verboseLogging = YES;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000064 }
65 return self;
66}
67
68#pragma mark - Public methods
69
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000070- (void)connectToRoom:(NSURL*)url {
71 NSURLRequest* request = [self getRequestFromUrl:url];
henrike@webrtc.org28e20752013-07-10 00:45:36 +000072 [NSURLConnection connectionWithRequest:request delegate:self];
73}
74
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000075- (void)sendData:(NSData*)data {
fischman@webrtc.org7c82ada2014-04-30 00:17:47 +000076 [self maybeLogMessage:@"Send message"];
77
78 dispatch_async(self.backgroundQueue, ^{
henrike@webrtc.org28e20752013-07-10 00:45:36 +000079 [self.sendQueue addObject:[data copy]];
fischman@webrtc.org7c82ada2014-04-30 00:17:47 +000080
81 if ([self.postMessageUrl length] < 1) {
82 return;
83 }
84 for (NSData* data in self.sendQueue) {
85 NSString* url =
86 [NSString stringWithFormat:@"%@/%@",
87 self.baseURL, self.postMessageUrl];
88 [self sendData:data withUrl:url];
89 }
90 [self.sendQueue removeAllObjects];
91 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +000092}
93
94#pragma mark - Internal methods
95
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +000096- (NSString*)findVar:(NSString*)name strippingQuotes:(BOOL)strippingQuotes {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +000097 NSError* error;
98 NSString* pattern =
99 [NSString stringWithFormat:@".*\n *var %@ = ([^\n]*);\n.*", name];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000100 NSRegularExpression* regexp =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000101 [NSRegularExpression regularExpressionWithPattern:pattern
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000102 options:0
103 error:&error];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000104 NSAssert(!error,
105 @"Unexpected error compiling regex: ",
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000106 error.localizedDescription);
107
108 NSRange fullRange = NSMakeRange(0, [self.roomHtml length]);
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000109 NSArray* matches =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000110 [regexp matchesInString:self.roomHtml options:0 range:fullRange];
111 if ([matches count] != 1) {
112 [self showMessage:[NSString stringWithFormat:@"%d matches for %@ in %@",
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000113 [matches count],
114 name,
115 self.roomHtml]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000116 return nil;
117 }
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000118 NSRange matchRange = [matches[0] rangeAtIndex:1];
119 NSString* value = [self.roomHtml substringWithRange:matchRange];
120 if (strippingQuotes) {
121 NSAssert([value length] > 2,
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000122 @"Can't strip quotes from short string: [%@]",
123 value);
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000124 NSAssert(([value characterAtIndex:0] == '\'' &&
125 [value characterAtIndex:[value length] - 1] == '\''),
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000126 @"Can't strip quotes from unquoted string: [%@]",
127 value);
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000128 value = [value substringWithRange:NSMakeRange(1, [value length] - 2)];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000129 }
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000130 return value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000131}
132
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000133- (NSURLRequest*)getRequestFromUrl:(NSURL*)url {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000134 self.roomHtml = [NSMutableString stringWithCapacity:20000];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000135 NSString* path =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000136 [NSString stringWithFormat:@"https:%@", [url resourceSpecifier]];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000137 NSURLRequest* request =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138 [NSURLRequest requestWithURL:[NSURL URLWithString:path]];
139 return request;
140}
141
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000142- (void)maybeLogMessage:(NSString*)message {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000143 if (self.verboseLogging) {
144 NSLog(@"%@", message);
145 }
146}
147
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000148- (void)sendData:(NSData*)data withUrl:(NSString*)url {
149 NSMutableURLRequest* request =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000150 [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
151 request.HTTPMethod = @"POST";
152 [request setHTTPBody:data];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000153 NSURLResponse* response;
154 NSError* error;
155 NSData* responseData = [NSURLConnection sendSynchronousRequest:request
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000156 returningResponse:&response
157 error:&error];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000158 NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000159 int status = [httpResponse statusCode];
160 NSAssert(status == 200,
161 @"Bad response [%d] to message: %@\n\n%@",
162 status,
163 [NSString stringWithUTF8String:[data bytes]],
164 [NSString stringWithUTF8String:[responseData bytes]]);
165}
166
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000167- (void)showMessage:(NSString*)message {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000168 NSLog(@"%@", message);
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000169 UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"Unable to join"
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000170 message:message
171 delegate:nil
172 cancelButtonTitle:@"OK"
173 otherButtonTitles:nil];
174 [alertView show];
175}
176
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000177- (void)updateICEServers:(NSMutableArray*)ICEServers
178 withTurnServer:(NSString*)turnServerUrl {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000179 if ([turnServerUrl length] < 1) {
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000180 [self.ICEServerDelegate onICEServers:ICEServers];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000181 return;
182 }
183 dispatch_async(self.backgroundQueue, ^(void) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000184 NSMutableURLRequest* request = [NSMutableURLRequest
185 requestWithURL:[NSURL URLWithString:turnServerUrl]];
186 [request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"];
187 [request addValue:@"https://apprtc.appspot.com"
188 forHTTPHeaderField:@"origin"];
189 NSURLResponse* response;
190 NSError* error;
191 NSData* responseData = [NSURLConnection sendSynchronousRequest:request
192 returningResponse:&response
193 error:&error];
194 if (!error) {
195 NSDictionary* json =
196 [NSJSONSerialization JSONObjectWithData:responseData
197 options:0
198 error:&error];
199 NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
200 NSString* username = json[@"username"];
201 NSString* password = json[@"password"];
202 NSArray* uris = json[@"uris"];
203 for (int i = 0; i < [uris count]; ++i) {
204 NSString* turnServer = [uris objectAtIndex:i];
205 RTCICEServer* ICEServer =
206 [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:turnServer]
207 username:username
208 password:password];
209 NSLog(@"Added ICE Server: %@", ICEServer);
210 [ICEServers addObject:ICEServer];
211 }
212 } else {
213 NSLog(@"Unable to get TURN server. Error: %@", error.description);
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000214 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000215
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000216 dispatch_async(dispatch_get_main_queue(), ^(void) {
217 [self.ICEServerDelegate onICEServers:ICEServers];
218 });
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000219 });
220}
221
222#pragma mark - NSURLConnectionDataDelegate methods
223
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000224- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
225 NSString* roomHtml = [NSString stringWithUTF8String:[data bytes]];
226 [self maybeLogMessage:[NSString stringWithFormat:@"Received %d chars",
227 [roomHtml length]]];
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000228 [self.roomHtml appendString:roomHtml];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000229}
230
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000231- (void)connection:(NSURLConnection*)connection
232 didReceiveResponse:(NSURLResponse*)response {
233 NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000234 int statusCode = [httpResponse statusCode];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000235 [self
236 maybeLogMessage:
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000237 [NSString stringWithFormat:
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000238 @"Response received\nURL\n%@\nStatus [%d]\nHeaders\n%@",
239 [httpResponse URL],
240 statusCode,
241 [httpResponse allHeaderFields]]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000242 NSAssert(statusCode == 200, @"Invalid response of %d received.", statusCode);
243}
244
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000245- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000246 [self maybeLogMessage:[NSString stringWithFormat:@"finished loading %d chars",
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000247 [self.roomHtml length]]];
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000248 NSRegularExpression* fullRegex =
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000249 [NSRegularExpression regularExpressionWithPattern:@"room is full"
250 options:0
251 error:nil];
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000252 if ([fullRegex
253 numberOfMatchesInString:self.roomHtml
254 options:0
255 range:NSMakeRange(0, [self.roomHtml length])]) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000256 [self showMessage:@"Room full"];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000257 APPRTCAppDelegate* ad =
258 (APPRTCAppDelegate*)[[UIApplication sharedApplication] delegate];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000259 [ad closeVideoUI];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000260 return;
261 }
262
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000263 NSString* fullUrl = [[[connection originalRequest] URL] absoluteString];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000264 NSRange queryRange = [fullUrl rangeOfString:@"?"];
265 self.baseURL = [fullUrl substringToIndex:queryRange.location];
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000266 [self maybeLogMessage:[NSString
267 stringWithFormat:@"Base URL: %@", self.baseURL]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000268
fischman@webrtc.org82387e42014-02-10 18:47:11 +0000269 self.initiator = [[self findVar:@"initiator" strippingQuotes:NO] boolValue];
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000270 self.token = [self findVar:@"channelToken" strippingQuotes:YES];
271 if (!self.token)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000272 return;
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000273 [self maybeLogMessage:[NSString stringWithFormat:@"Token: %@", self.token]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000275 NSString* roomKey = [self findVar:@"roomKey" strippingQuotes:YES];
276 NSString* me = [self findVar:@"me" strippingQuotes:YES];
277 if (!roomKey || !me)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000278 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000279 self.postMessageUrl =
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000280 [NSString stringWithFormat:@"/message?r=%@&u=%@", roomKey, me];
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000281 [self maybeLogMessage:[NSString stringWithFormat:@"POST message URL: %@",
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000282 self.postMessageUrl]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000283
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000284 NSString* pcConfig = [self findVar:@"pcConfig" strippingQuotes:NO];
285 if (!pcConfig)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000286 return;
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000287 [self maybeLogMessage:[NSString
288 stringWithFormat:@"PC Config JSON: %@", pcConfig]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000290 NSString* turnServerUrl = [self findVar:@"turnUrl" strippingQuotes:YES];
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000291 if (turnServerUrl) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000292 [self maybeLogMessage:[NSString
293 stringWithFormat:@"TURN server request URL: %@",
294 turnServerUrl]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000295 }
296
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000297 NSError* error;
298 NSData* pcData = [pcConfig dataUsingEncoding:NSUTF8StringEncoding];
299 NSDictionary* json =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000300 [NSJSONSerialization JSONObjectWithData:pcData options:0 error:&error];
301 NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000302 NSArray* servers = [json objectForKey:@"iceServers"];
303 NSMutableArray* ICEServers = [NSMutableArray array];
304 for (NSDictionary* server in servers) {
305 NSString* url = [server objectForKey:@"urls"];
306 NSString* username = json[@"username"];
307 NSString* credential = [server objectForKey:@"credential"];
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000308 if (!username) {
309 username = @"";
310 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000311 if (!credential) {
312 credential = @"";
313 }
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000314 [self maybeLogMessage:[NSString
315 stringWithFormat:@"url [%@] - credential [%@]",
316 url,
317 credential]];
318 RTCICEServer* ICEServer =
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000319 [[RTCICEServer alloc] initWithURI:[NSURL URLWithString:url]
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000320 username:username
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000321 password:credential];
fischman@webrtc.orgc31d4d02013-09-05 21:49:58 +0000322 NSLog(@"Added ICE Server: %@", ICEServer);
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000323 [ICEServers addObject:ICEServer];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000324 }
fischman@webrtc.org1bc19542013-08-01 18:29:45 +0000325 [self updateICEServers:ICEServers withTurnServer:turnServerUrl];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000326
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000327 NSString* mc = [self findVar:@"mediaConstraints" strippingQuotes:NO];
328 if (mc) {
329 error = nil;
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000330 NSData* mcData = [mc dataUsingEncoding:NSUTF8StringEncoding];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000331 json =
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000332 [NSJSONSerialization JSONObjectWithData:mcData options:0 error:&error];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000333 NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
334 if ([[json objectForKey:@"video"] boolValue]) {
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000335 _videoConstraints = [[RTCMediaConstraints alloc] init];
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000336 }
337 }
338
fischman@webrtc.org7fa1fcb2014-03-25 00:11:56 +0000339 [self
340 maybeLogMessage:[NSString
341 stringWithFormat:@"About to open GAE with token: %@",
342 self.token]];
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000343 self.gaeChannel =
344 [[GAEChannelClient alloc] initWithToken:self.token
345 delegate:self.messageHandler];
346}
347
348@end