Advertisement
Guest User

Untitled

a guest
May 22nd, 2016
266
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.76 KB | None | 0 0
  1. #include "WWWConnection.h"
  2.  
  3. // WARNING: this MUST be c decl (NSString ctor will be called after +load, so we cant really change its value)
  4.  
  5. // If you need to communicate with HTTPS server with self signed certificate you might consider UnityWWWConnectionSelfSignedCertDelegate
  6. // Though use it on your own risk. Blindly accepting self signed certificate is prone to MITM attack
  7.  
  8. //const char* WWWDelegateClassName = "UnityWWWConnectionSelfSignedCertDelegate";
  9. const char* WWWDelegateClassName = "UnityWWWConnectionDelegate";
  10. const char* WWWRequestProviderClassName = "UnityWWWRequestDefaultProvider";
  11.  
  12. @interface UnityWWWConnectionDelegate()
  13. @property (readwrite, nonatomic) void* udata;
  14. @property (readwrite, retain, nonatomic) NSURL* url;
  15. @property (readwrite, retain, nonatomic) NSString* user;
  16. @property (readwrite, retain, nonatomic) NSString* password;
  17. @property (readwrite, retain, nonatomic) NSMutableURLRequest* request;
  18. @property (readwrite, retain, nonatomic) NSURLConnection* connection;
  19. @property (strong, nonatomic) NSCondition* condition;
  20. @end
  21.  
  22.  
  23. @implementation UnityWWWConnectionDelegate
  24. {
  25. // link to unity WWW implementation
  26. void* _udata;
  27.  
  28. // connection parameters
  29. NSMutableURLRequest* _request;
  30. // connection that we manage
  31. NSURLConnection* _connection;
  32.  
  33. // NSURLConnection do not quite handle user:pass@host urls
  34. // so we need to extract user/pass ourselves
  35. NSURL* _url;
  36. NSString* _user;
  37. NSString* _password;
  38.  
  39. // response
  40. NSString* _responseHeader;
  41. NSInteger _status;
  42. size_t _estimatedLength;
  43. size_t _dataRecievd;
  44. int _retryCount;
  45. }
  46.  
  47. @synthesize url = _url;
  48. @synthesize user = _user;
  49. @synthesize password = _password;
  50. @synthesize request = _request;
  51. @synthesize connection = _connection;
  52.  
  53. @synthesize udata = _udata;
  54. @synthesize shouldAbort;
  55.  
  56. - (NSURL*)extractUserPassFromUrl:(NSURL*)url
  57. {
  58. self.user = url.user;
  59. self.password = url.password;
  60.  
  61. // strip user/pass from url
  62. NSString* newUrl = [NSString stringWithFormat:@"%@://%@%s%s%@%s%s",
  63. url.scheme, url.host,
  64. url.port ? ":" : "", url.port ? [[url.port stringValue] UTF8String] : "",
  65. url.path,
  66. url.fragment ? "#" : "", url.fragment ? [url.fragment UTF8String] : ""
  67. ];
  68. return [NSURL URLWithString:newUrl];
  69. }
  70.  
  71. - (id)initWithURL:(NSURL*)url udata:(void*)udata;
  72. {
  73. self->_retryCount = 0;
  74. if((self = [super init]))
  75. {
  76. self.url = url.user != nil ? [self extractUserPassFromUrl:url] : url;
  77. self.udata = udata;
  78.  
  79. if([url.scheme caseInsensitiveCompare:@"http"] == NSOrderedSame)
  80. NSLog(@"You are using download over http. Currently unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition, but it will be removed soon. Please consider updating to https.");
  81. }
  82.  
  83. return self;
  84. }
  85.  
  86. + (id)newDelegateWithURL:(NSURL*)url udata:(void*)udata
  87. {
  88. Class target = NSClassFromString([NSString stringWithUTF8String:WWWDelegateClassName]);
  89. NSAssert([target isSubclassOfClass:[UnityWWWConnectionDelegate class]], @"You MUST subclass UnityWWWConnectionDelegate");
  90.  
  91. return [[target alloc] initWithURL:url udata:udata];
  92. }
  93.  
  94. + (id)newDelegateWithCStringURL:(const char*)url udata:(void*)udata
  95. {
  96. return [UnityWWWConnectionDelegate newDelegateWithURL:[NSURL URLWithString:[NSString stringWithUTF8String: url]] udata:udata];
  97. }
  98.  
  99. + (NSMutableURLRequest*)newRequestForHTTPMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers
  100. {
  101. Class target = NSClassFromString([NSString stringWithUTF8String:WWWRequestProviderClassName]);
  102. NSAssert([target conformsToProtocol:@protocol(UnityWWWRequestProvider)], @"You MUST implement UnityWWWRequestProvider protocol");
  103.  
  104. return [target allocRequestForHTTPMethod:method url:url headers:headers];
  105. }
  106.  
  107. - (void)cleanup
  108. {
  109. [_connection cancel];
  110. self.condition = nil;
  111. _connection = nil;
  112. _request = nil;
  113. }
  114.  
  115. - (NSURLRequest *)connection:(NSURLConnection *)connection
  116. willSendRequest:(NSURLRequest *)request
  117. redirectResponse:(NSURLResponse *)response;
  118. {
  119. if (response)
  120. {
  121. // notify TransportiPhone of the redirect and signal to process the next response.
  122. if([response isKindOfClass:[NSHTTPURLResponse class]])
  123. {
  124. NSHTTPURLResponse *httpresponse = (NSHTTPURLResponse*)response;
  125. NSMutableDictionary *headers = [httpresponse.allHeaderFields mutableCopy];
  126. // grab the correct URL from the request that would have
  127. // automatically been called through NSURLConnection.
  128. // The reason we do this is that WebRequestProto's state needs to
  129. // get updated internally, so we intercept redirects, cancel the current
  130. // NSURLConnection, notify WebRequestProto and let it construct a new
  131. // request from the updated URL
  132. [headers setObject:[request.URL absoluteString] forKey:@"Location"];
  133. httpresponse = [[NSHTTPURLResponse alloc] initWithURL:response.URL statusCode:httpresponse.statusCode HTTPVersion:nil headerFields:headers];
  134. [self handleResponse:httpresponse];
  135. }
  136. else
  137. {
  138. [self handleResponse:response];
  139. }
  140. SignalConnection(self);
  141. [connection cancel];
  142. return nil;
  143. }
  144. return request;
  145. }
  146.  
  147. - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
  148. {
  149. [self handleResponse:response];
  150. }
  151.  
  152. -(void)handleResponse:(NSURLResponse*)response
  153. {
  154. // on ios pre-5.0 NSHTTPURLResponse was not created for "file://"" connections, so play safe here
  155. // TODO: remove that once we have 5.0 as requirement
  156. self->_status = 200;
  157. if([response isMemberOfClass:[NSHTTPURLResponse class]])
  158. {
  159. NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
  160. NSDictionary* respHeader = [httpResponse allHeaderFields];
  161. NSEnumerator* headerEnum = [respHeader keyEnumerator];
  162.  
  163. self->_status = [httpResponse statusCode];
  164.  
  165. NSMutableString* headerString = [NSMutableString stringWithCapacity:1024];
  166. [headerString appendFormat:@"Status: HTTP/1.1 %d %@\n", (int)self->_status,
  167. [NSHTTPURLResponse localizedStringForStatusCode:self->_status]];
  168.  
  169. for(id headerKey = [headerEnum nextObject] ; headerKey ; headerKey = [headerEnum nextObject])
  170. [headerString appendFormat:@"%@: %@\n", (NSString*)headerKey, (NSString*)[respHeader objectForKey:headerKey]];
  171.  
  172. self->_responseHeader = headerString;
  173.  
  174. long long contentLength = [response expectedContentLength];
  175.  
  176. // ignore any data that we might have recieved during a redirect
  177. self->_estimatedLength = contentLength > 0 && (self->_status / 100 != 3) ? contentLength : 0;
  178. self->_dataRecievd = 0;
  179.  
  180. // status 2xx are all success
  181. // in case of error status we do not cancel right away to actually get server response:
  182. // sometimes it might contain info useful for developers (custom webapp)
  183. // instead we just keep on getting data while it is here and simply remember that there was error
  184. // status 3xx are all redirects, we should not report them as errors as well
  185. if(self->_status / 100 > 3)
  186. {
  187. UnityReportWWWStatusError(self.udata, (int)self->_status, [[NSHTTPURLResponse localizedStringForStatusCode: self->_status] UTF8String]);
  188. }
  189. }
  190.  
  191. UnityReportWWWReceivedResponse(self.udata, (int)self->_status, (unsigned int)(unsigned long)self->_estimatedLength, [self->_responseHeader UTF8String]);
  192. }
  193.  
  194. - (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
  195. {
  196. UnityReportWWWReceivedData(self.udata, data.bytes, (unsigned int)[data length], (unsigned int)self->_estimatedLength);
  197. if(self.shouldAbort)
  198. {
  199. [connection cancel];
  200. SignalConnection(self);
  201. }
  202. }
  203.  
  204. static void SignalConnection(UnityWWWConnectionDelegate* delegate)
  205. {
  206. // Signal the condition variable in case it is waiting
  207. NSCondition *condition = delegate.condition;
  208.  
  209. delegate.request = nil;
  210. delegate.condition = nil;
  211.  
  212. [condition lock];
  213. [condition signal];
  214. [condition unlock];
  215. }
  216.  
  217. static void WaitOnCondition(UnityWWWConnectionDelegate* delegate)
  218. {
  219. NSCondition *condition = delegate.condition;
  220. [condition lock];
  221. [condition wait];
  222. [condition unlock];
  223. }
  224.  
  225. - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
  226. {
  227. UnityReportWWWStatusError(self.udata, (int)[error code], [[error localizedDescription] UTF8String]);
  228. SignalConnection(self);
  229. }
  230.  
  231. - (void)connectionDidFinishLoading:(NSURLConnection*)connection
  232. {
  233. self.connection = nil;
  234. UnityReportWWWFinishedLoadingData(self.udata);
  235. SignalConnection(self);
  236. }
  237.  
  238. - (void)connection:(NSURLConnection*)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
  239. {
  240. UnityReportWWWSentData(self.udata, (unsigned int)totalBytesWritten, (unsigned int)totalBytesExpectedToWrite);
  241. }
  242.  
  243. - (BOOL)connection:(NSURLConnection*)connection handleAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
  244. {
  245. return NO;
  246. }
  247. - (void)connection:(NSURLConnection*)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
  248. {
  249.  
  250. if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodServerTrust) {
  251. [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
  252. }
  253. else
  254. {
  255.  
  256. BOOL authHandled = [self connection:connection handleAuthenticationChallenge:challenge];
  257.  
  258. if(authHandled == NO)
  259. {
  260. self->_retryCount++;
  261.  
  262. // Empty user or password
  263. if(self->_retryCount > 1 || self.user == nil || [self.user length] == 0 || self.password == nil || [self.password length] == 0)
  264. {
  265. [[challenge sender] cancelAuthenticationChallenge:challenge];
  266. return;
  267. }
  268.  
  269. NSURLCredential* newCredential =
  270. [NSURLCredential credentialWithUser:self.user password:self.password persistence:NSURLCredentialPersistenceNone];
  271.  
  272. [challenge.sender useCredential:newCredential forAuthenticationChallenge:challenge];
  273. }
  274. }
  275. }
  276.  
  277. @end
  278.  
  279.  
  280. @implementation UnityWWWConnectionSelfSignedCertDelegate
  281.  
  282. - (BOOL)connection:(NSURLConnection*)connection handleAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
  283. {
  284. if([[challenge.protectionSpace authenticationMethod] isEqualToString:@"NSURLAuthenticationMethodServerTrust"])
  285. {
  286. [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]
  287. forAuthenticationChallenge:challenge];
  288.  
  289. return YES;
  290. }
  291.  
  292. return [super connection:connection handleAuthenticationChallenge:challenge];
  293. }
  294.  
  295. @end
  296.  
  297.  
  298. @implementation UnityWWWRequestDefaultProvider
  299. + (NSMutableURLRequest*)allocRequestForHTTPMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers
  300. {
  301. NSMutableURLRequest* request = [[NSMutableURLRequest alloc] init];
  302. [request setURL:url];
  303. [request setHTTPMethod:method];
  304. [request setAllHTTPHeaderFields:headers];
  305.  
  306. return request;
  307. }
  308. @end
  309.  
  310. //
  311. // unity interface
  312. //
  313.  
  314. extern "C" void UnitySendWWWConnection(void* connection, const void* data, unsigned length, bool blockImmediately)
  315. {
  316. UnityWWWConnectionDelegate* delegate = (__bridge UnityWWWConnectionDelegate*)connection;
  317.  
  318. NSMutableURLRequest* request = delegate.request;
  319.  
  320. if (data != nil && length > 0)
  321. {
  322. [request setHTTPBody:[NSData dataWithBytes:data length:length]];
  323. [request setValue:[NSString stringWithFormat:@"%d", length] forHTTPHeaderField:@"Content-Length"];
  324. }
  325.  
  326. dispatch_async(dispatch_get_main_queue(), ^{
  327. delegate.connection = [NSURLConnection connectionWithRequest:request delegate:delegate];
  328. });
  329.  
  330. if (blockImmediately)
  331. {
  332. WaitOnCondition(delegate);
  333. }
  334. }
  335.  
  336. extern "C" void* UnityStartWWWConnectionCustom(void* udata, const char* methodString, const void* headerDict, const char* url)
  337. {
  338. UnityWWWConnectionDelegate* delegate = [UnityWWWConnectionDelegate newDelegateWithCStringURL:url udata:udata];
  339.  
  340. delegate.request = [UnityWWWConnectionDelegate newRequestForHTTPMethod:[NSString stringWithUTF8String:methodString] url:delegate.url headers:(__bridge NSDictionary*)headerDict];
  341.  
  342. // Initialize the condition variable
  343. delegate.condition = [[NSCondition alloc] init];
  344.  
  345. return (__bridge_retained void*)delegate;
  346. }
  347.  
  348. extern "C" bool UnityBlockWWWConnectionIsDone(void* connection)
  349. {
  350. UnityWWWConnectionDelegate* delegate = (__bridge UnityWWWConnectionDelegate*)connection;
  351. return (delegate.request == nil);
  352. }
  353.  
  354. extern "C" void UnityBlockWWWConnectionUntilDone(void* connection)
  355. {
  356. UnityWWWConnectionDelegate* delegate = (__bridge UnityWWWConnectionDelegate*)connection;
  357. WaitOnCondition(delegate);
  358. }
  359.  
  360. extern "C" void UnityDestroyWWWConnection(void* connection)
  361. {
  362. UnityWWWConnectionDelegate* delegate = (__bridge_transfer UnityWWWConnectionDelegate*)connection;
  363.  
  364. [delegate cleanup];
  365. delegate = nil;
  366. }
  367.  
  368. extern "C" void UnityShouldCancelWWW(const void* connection)
  369. {
  370. ((__bridge UnityWWWConnectionDelegate*)connection).shouldAbort = YES;
  371. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement