RCTHTTPRequestHandler.mm 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import <React/RCTHTTPRequestHandler.h>
  8. #import <mutex>
  9. #import <React/RCTNetworking.h>
  10. #import <ReactCommon/RCTTurboModule.h>
  11. #import "RCTNetworkPlugins.h"
  12. @interface RCTHTTPRequestHandler () <NSURLSessionDataDelegate, RCTTurboModule>
  13. @end
  14. @implementation RCTHTTPRequestHandler
  15. {
  16. NSMapTable *_delegates;
  17. NSURLSession *_session;
  18. std::mutex _mutex;
  19. }
  20. @synthesize bridge = _bridge;
  21. @synthesize methodQueue = _methodQueue;
  22. RCT_EXPORT_MODULE()
  23. - (void)invalidate
  24. {
  25. std::lock_guard<std::mutex> lock(_mutex);
  26. [self->_session invalidateAndCancel];
  27. self->_session = nil;
  28. }
  29. // Needs to lock before call this method.
  30. - (BOOL)isValid
  31. {
  32. // if session == nil and delegates != nil, we've been invalidated
  33. return _session || !_delegates;
  34. }
  35. #pragma mark - NSURLRequestHandler
  36. - (BOOL)canHandleRequest:(NSURLRequest *)request
  37. {
  38. static NSSet<NSString *> *schemes = nil;
  39. static dispatch_once_t onceToken;
  40. dispatch_once(&onceToken, ^{
  41. // technically, RCTHTTPRequestHandler can handle file:// as well,
  42. // but it's less efficient than using RCTFileRequestHandler
  43. schemes = [[NSSet alloc] initWithObjects:@"http", @"https", nil];
  44. });
  45. return [schemes containsObject:request.URL.scheme.lowercaseString];
  46. }
  47. - (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request
  48. withDelegate:(id<RCTURLRequestDelegate>)delegate
  49. {
  50. std::lock_guard<std::mutex> lock(_mutex);
  51. // Lazy setup
  52. if (!_session && [self isValid]) {
  53. // You can override default NSURLSession instance property allowsCellularAccess (default value YES)
  54. // by providing the following key to your RN project (edit ios/project/Info.plist file in Xcode):
  55. // <key>ReactNetworkForceWifiOnly</key> <true/>
  56. // This will set allowsCellularAccess to NO and force Wifi only for all network calls on iOS
  57. // If you do not want to override default behavior, do nothing or set key with value false
  58. NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
  59. NSNumber *useWifiOnly = [infoDictionary objectForKey:@"ReactNetworkForceWifiOnly"];
  60. NSOperationQueue *callbackQueue = [NSOperationQueue new];
  61. callbackQueue.maxConcurrentOperationCount = 1;
  62. callbackQueue.underlyingQueue = [[_bridge networking] methodQueue];
  63. NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
  64. // Set allowsCellularAccess to NO ONLY if key ReactNetworkForceWifiOnly exists AND its value is YES
  65. if (useWifiOnly) {
  66. configuration.allowsCellularAccess = ![useWifiOnly boolValue];
  67. }
  68. [configuration setHTTPShouldSetCookies:YES];
  69. [configuration setHTTPCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
  70. [configuration setHTTPCookieStorage:[NSHTTPCookieStorage sharedHTTPCookieStorage]];
  71. _session = [NSURLSession sessionWithConfiguration:configuration
  72. delegate:self
  73. delegateQueue:callbackQueue];
  74. _delegates = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
  75. valueOptions:NSPointerFunctionsStrongMemory
  76. capacity:0];
  77. }
  78. NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
  79. [_delegates setObject:delegate forKey:task];
  80. [task resume];
  81. return task;
  82. }
  83. - (void)cancelRequest:(NSURLSessionDataTask *)task
  84. {
  85. {
  86. std::lock_guard<std::mutex> lock(_mutex);
  87. [_delegates removeObjectForKey:task];
  88. }
  89. [task cancel];
  90. }
  91. #pragma mark - NSURLSession delegate
  92. - (void)URLSession:(NSURLSession *)session
  93. task:(NSURLSessionTask *)task
  94. didSendBodyData:(int64_t)bytesSent
  95. totalBytesSent:(int64_t)totalBytesSent
  96. totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
  97. {
  98. id<RCTURLRequestDelegate> delegate;
  99. {
  100. std::lock_guard<std::mutex> lock(_mutex);
  101. delegate = [_delegates objectForKey:task];
  102. }
  103. [delegate URLRequest:task didSendDataWithProgress:totalBytesSent];
  104. }
  105. - (void)URLSession:(NSURLSession *)session
  106. task:(NSURLSessionTask *)task
  107. willPerformHTTPRedirection:(NSHTTPURLResponse *)response
  108. newRequest:(NSURLRequest *)request
  109. completionHandler:(void (^)(NSURLRequest *))completionHandler
  110. {
  111. // Reset the cookies on redirect.
  112. // This is necessary because we're not letting iOS handle cookies by itself
  113. NSMutableURLRequest *nextRequest = [request mutableCopy];
  114. NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:request.URL];
  115. nextRequest.allHTTPHeaderFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
  116. completionHandler(nextRequest);
  117. }
  118. - (void)URLSession:(NSURLSession *)session
  119. dataTask:(NSURLSessionDataTask *)task
  120. didReceiveResponse:(NSURLResponse *)response
  121. completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
  122. {
  123. id<RCTURLRequestDelegate> delegate;
  124. {
  125. std::lock_guard<std::mutex> lock(_mutex);
  126. delegate = [_delegates objectForKey:task];
  127. }
  128. [delegate URLRequest:task didReceiveResponse:response];
  129. completionHandler(NSURLSessionResponseAllow);
  130. }
  131. - (void)URLSession:(NSURLSession *)session
  132. dataTask:(NSURLSessionDataTask *)task
  133. didReceiveData:(NSData *)data
  134. {
  135. id<RCTURLRequestDelegate> delegate;
  136. {
  137. std::lock_guard<std::mutex> lock(_mutex);
  138. delegate = [_delegates objectForKey:task];
  139. }
  140. [delegate URLRequest:task didReceiveData:data];
  141. }
  142. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
  143. {
  144. id<RCTURLRequestDelegate> delegate;
  145. {
  146. std::lock_guard<std::mutex> lock(_mutex);
  147. delegate = [_delegates objectForKey:task];
  148. [_delegates removeObjectForKey:task];
  149. }
  150. [delegate URLRequest:task didCompleteWithError:error];
  151. }
  152. @end
  153. Class RCTHTTPRequestHandlerCls(void) {
  154. return RCTHTTPRequestHandler.class;
  155. }