FIRCLSFABNetworkClient.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // Copyright 2019 Google
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "FIRCLSFABNetworkClient.h"
  15. #if FIRCLSURLSESSION_REQUIRED
  16. #import "FIRCLSURLSession.h"
  17. #endif
  18. #import "FIRCLSNetworkResponseHandler.h"
  19. static const float FIRCLSNetworkMinimumRetryJitter = 0.90f;
  20. static const float FIRCLSNetworkMaximumRetryJitter = 1.10f;
  21. const NSUInteger FIRCLSNetworkMaximumRetryCount = 10;
  22. @interface FIRCLSFABNetworkClient () <NSURLSessionDelegate, NSURLSessionTaskDelegate>
  23. @property(nonatomic, strong, readonly) NSURLSession *session;
  24. @end
  25. @implementation FIRCLSFABNetworkClient
  26. - (instancetype)init {
  27. return [self initWithQueue:nil];
  28. }
  29. - (instancetype)initWithQueue:(nullable NSOperationQueue *)operationQueue {
  30. #if !FIRCLSURLSESSION_REQUIRED
  31. NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  32. #else
  33. NSURLSessionConfiguration *config = [FIRCLSURLSessionConfiguration defaultSessionConfiguration];
  34. #endif
  35. return [self initWithSessionConfiguration:config queue:operationQueue];
  36. }
  37. - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)config
  38. queue:(nullable NSOperationQueue *)operationQueue {
  39. self = [super init];
  40. if (!self) {
  41. return nil;
  42. }
  43. #if !FIRCLSURLSESSION_REQUIRED
  44. _session = [NSURLSession sessionWithConfiguration:config
  45. delegate:self
  46. delegateQueue:operationQueue];
  47. #else
  48. _session = [FIRCLSURLSession sessionWithConfiguration:config
  49. delegate:self
  50. delegateQueue:operationQueue];
  51. #endif
  52. if (!_session) {
  53. return nil;
  54. }
  55. return self;
  56. }
  57. - (void)dealloc {
  58. [_session finishTasksAndInvalidate];
  59. }
  60. #pragma mark - Delay Handling
  61. - (double)randomDoubleWithMin:(double)min max:(double)max {
  62. return min + ((max - min) * drand48());
  63. }
  64. - (double)generateRandomJitter {
  65. return [self randomDoubleWithMin:FIRCLSNetworkMinimumRetryJitter
  66. max:FIRCLSNetworkMaximumRetryJitter];
  67. }
  68. - (NSTimeInterval)computeDelayForResponse:(NSURLResponse *)response
  69. withRetryCount:(NSUInteger)count {
  70. NSTimeInterval initialValue = [FIRCLSNetworkResponseHandler retryValueForResponse:response];
  71. // make sure count is > 0
  72. count = MAX(count, 1);
  73. // make sure initialValue is >2 for exponential backoff to work reasonably with low count numbers
  74. initialValue = MAX(initialValue, 2.0);
  75. const double jitter = [self generateRandomJitter];
  76. return pow(initialValue, count) * jitter; // exponential backoff
  77. }
  78. - (void)runAfterRetryValueFromResponse:(NSURLResponse *)response
  79. attempts:(NSUInteger)count
  80. onQueue:(dispatch_queue_t)queue
  81. block:(void (^)(void))block {
  82. const NSTimeInterval delay = [self computeDelayForResponse:response withRetryCount:count];
  83. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(delay * NSEC_PER_SEC)), queue, block);
  84. }
  85. - (void)runAfterRetryValueFromResponse:(NSURLResponse *)response
  86. attempts:(NSUInteger)count
  87. block:(void (^)(void))block {
  88. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  89. [self runAfterRetryValueFromResponse:response attempts:count onQueue:queue block:block];
  90. }
  91. #pragma mark - Tasks
  92. - (void)startDataTaskWithRequest:(NSURLRequest *)request
  93. retryLimit:(NSUInteger)retryLimit
  94. tries:(NSUInteger)tries
  95. completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler {
  96. NSURLSessionTask *task = [self.session
  97. dataTaskWithRequest:request
  98. completionHandler:^(NSData *data, NSURLResponse *response, NSError *taskError) {
  99. [FIRCLSNetworkResponseHandler
  100. handleCompletedResponse:response
  101. forOriginalRequest:request
  102. error:taskError
  103. block:^(BOOL retry, NSError *error) {
  104. if (!retry) {
  105. completionHandler(data, response, error);
  106. return;
  107. }
  108. if (tries >= retryLimit) {
  109. NSDictionary *userInfo = @{
  110. @"retryLimit" : @(retryLimit),
  111. NSURLErrorFailingURLStringErrorKey : request.URL
  112. };
  113. completionHandler(
  114. nil, nil,
  115. [NSError
  116. errorWithDomain:FIRCLSNetworkErrorDomain
  117. code:FIRCLSNetworkErrorMaximumAttemptsReached
  118. userInfo:userInfo]);
  119. return;
  120. }
  121. [self
  122. runAfterRetryValueFromResponse:response
  123. attempts:tries
  124. block:^{
  125. [self
  126. startDataTaskWithRequest:
  127. request
  128. retryLimit:
  129. retryLimit
  130. tries:
  131. (tries +
  132. 1)
  133. completionHandler:
  134. completionHandler];
  135. }];
  136. }];
  137. }];
  138. [task resume];
  139. if (!task) {
  140. completionHandler(nil, nil,
  141. [NSError errorWithDomain:FIRCLSNetworkErrorDomain
  142. code:FIRCLSNetworkErrorFailedToStartOperation
  143. userInfo:nil]);
  144. }
  145. }
  146. - (void)startDataTaskWithRequest:(NSURLRequest *)request
  147. retryLimit:(NSUInteger)retryLimit
  148. completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler {
  149. [self startDataTaskWithRequest:request
  150. retryLimit:retryLimit
  151. tries:0
  152. completionHandler:completionHandler];
  153. }
  154. - (void)startDataTaskWithRequest:(NSURLRequest *)request
  155. completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler {
  156. [self startDataTaskWithRequest:request
  157. retryLimit:FIRCLSNetworkMaximumRetryCount
  158. completionHandler:completionHandler];
  159. }
  160. - (void)startDownloadTaskWithRequest:(NSURLRequest *)request
  161. retryLimit:(NSUInteger)retryLimit
  162. tries:(NSUInteger)tries
  163. completionHandler:
  164. (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler {
  165. NSURLSessionTask *task = [self.session
  166. downloadTaskWithRequest:request
  167. completionHandler:^(NSURL *location, NSURLResponse *response, NSError *taskError) {
  168. [FIRCLSNetworkResponseHandler
  169. handleCompletedResponse:response
  170. forOriginalRequest:request
  171. error:taskError
  172. block:^(BOOL retry, NSError *error) {
  173. if (!retry) {
  174. completionHandler(location, response, error);
  175. return;
  176. }
  177. if (tries >= retryLimit) {
  178. NSDictionary *userInfo = @{
  179. @"retryLimit" : @(retryLimit),
  180. NSURLErrorFailingURLStringErrorKey : request.URL
  181. };
  182. completionHandler(
  183. nil, nil,
  184. [NSError
  185. errorWithDomain:FIRCLSNetworkErrorDomain
  186. code:
  187. FIRCLSNetworkErrorMaximumAttemptsReached
  188. userInfo:userInfo]);
  189. return;
  190. }
  191. [self
  192. runAfterRetryValueFromResponse:response
  193. attempts:tries
  194. block:^{
  195. [self
  196. startDownloadTaskWithRequest:
  197. request
  198. retryLimit:
  199. retryLimit
  200. tries:
  201. (tries +
  202. 1)
  203. completionHandler:
  204. completionHandler];
  205. }];
  206. }];
  207. }];
  208. [task resume];
  209. if (!task) {
  210. completionHandler(nil, nil,
  211. [NSError errorWithDomain:FIRCLSNetworkErrorDomain
  212. code:FIRCLSNetworkErrorFailedToStartOperation
  213. userInfo:nil]);
  214. }
  215. }
  216. - (void)startDownloadTaskWithRequest:(NSURLRequest *)request
  217. retryLimit:(NSUInteger)retryLimit
  218. completionHandler:
  219. (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler {
  220. [self startDownloadTaskWithRequest:request
  221. retryLimit:retryLimit
  222. tries:0
  223. completionHandler:completionHandler];
  224. }
  225. - (void)startDownloadTaskWithRequest:(NSURLRequest *)request
  226. completionHandler:
  227. (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler {
  228. [self startDownloadTaskWithRequest:request
  229. retryLimit:FIRCLSNetworkMaximumRetryCount
  230. completionHandler:completionHandler];
  231. }
  232. - (void)invalidateAndCancel {
  233. [self.session invalidateAndCancel];
  234. }
  235. #pragma mark - NSURLSession Delegate
  236. - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
  237. }
  238. @end