FIRCLSFABNetworkClient.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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 "Crashlytics/Shared/FIRCLSNetworking/FIRCLSFABNetworkClient.h"
  15. #import "Crashlytics/Shared/FIRCLSNetworking/FIRCLSNetworkResponseHandler.h"
  16. static const float FIRCLSNetworkMinimumRetryJitter = 0.90f;
  17. static const float FIRCLSNetworkMaximumRetryJitter = 1.10f;
  18. const NSUInteger FIRCLSNetworkMaximumRetryCount = 10;
  19. @interface FIRCLSFABNetworkClient () <NSURLSessionDelegate, NSURLSessionTaskDelegate>
  20. @property(nonatomic, strong, readonly) NSURLSession *session;
  21. @end
  22. @implementation FIRCLSFABNetworkClient
  23. - (instancetype)init {
  24. return [self initWithQueue:nil];
  25. }
  26. - (instancetype)initWithQueue:(nullable NSOperationQueue *)operationQueue {
  27. NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  28. return [self initWithSessionConfiguration:config queue:operationQueue];
  29. }
  30. - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)config
  31. queue:(nullable NSOperationQueue *)operationQueue {
  32. self = [super init];
  33. if (!self) {
  34. return nil;
  35. }
  36. _session = [NSURLSession sessionWithConfiguration:config
  37. delegate:self
  38. delegateQueue:operationQueue];
  39. if (!_session) {
  40. return nil;
  41. }
  42. return self;
  43. }
  44. - (void)dealloc {
  45. [_session finishTasksAndInvalidate];
  46. }
  47. #pragma mark - Delay Handling
  48. - (double)randomDoubleWithMin:(double)min max:(double)max {
  49. return min + ((max - min) * drand48());
  50. }
  51. - (double)generateRandomJitter {
  52. return [self randomDoubleWithMin:FIRCLSNetworkMinimumRetryJitter
  53. max:FIRCLSNetworkMaximumRetryJitter];
  54. }
  55. - (NSTimeInterval)computeDelayForResponse:(NSURLResponse *)response
  56. withRetryCount:(NSUInteger)count {
  57. NSTimeInterval initialValue = [FIRCLSNetworkResponseHandler retryValueForResponse:response];
  58. // make sure count is > 0
  59. count = MAX(count, 1);
  60. // make sure initialValue is >2 for exponential backoff to work reasonably with low count numbers
  61. initialValue = MAX(initialValue, 2.0);
  62. const double jitter = [self generateRandomJitter];
  63. return pow(initialValue, count) * jitter; // exponential backoff
  64. }
  65. - (void)runAfterRetryValueFromResponse:(NSURLResponse *)response
  66. attempts:(NSUInteger)count
  67. onQueue:(dispatch_queue_t)queue
  68. block:(void (^)(void))block {
  69. const NSTimeInterval delay = [self computeDelayForResponse:response withRetryCount:count];
  70. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(delay * NSEC_PER_SEC)), queue, block);
  71. }
  72. - (void)runAfterRetryValueFromResponse:(NSURLResponse *)response
  73. attempts:(NSUInteger)count
  74. block:(void (^)(void))block {
  75. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  76. [self runAfterRetryValueFromResponse:response attempts:count onQueue:queue block:block];
  77. }
  78. #pragma mark - Tasks
  79. - (void)startDataTaskWithRequest:(NSURLRequest *)request
  80. retryLimit:(NSUInteger)retryLimit
  81. tries:(NSUInteger)tries
  82. completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler {
  83. NSURLSessionTask *task = [self.session
  84. dataTaskWithRequest:request
  85. completionHandler:^(NSData *data, NSURLResponse *response, NSError *taskError) {
  86. [FIRCLSNetworkResponseHandler
  87. handleCompletedResponse:response
  88. forOriginalRequest:request
  89. error:taskError
  90. block:^(BOOL retry, NSError *error) {
  91. if (!retry) {
  92. completionHandler(data, response, error);
  93. return;
  94. }
  95. if (tries >= retryLimit) {
  96. NSDictionary *userInfo = @{
  97. @"retryLimit" : @(retryLimit),
  98. NSURLErrorFailingURLStringErrorKey : request.URL
  99. };
  100. completionHandler(
  101. nil, nil,
  102. [NSError
  103. errorWithDomain:FIRCLSNetworkErrorDomain
  104. code:FIRCLSNetworkErrorMaximumAttemptsReached
  105. userInfo:userInfo]);
  106. return;
  107. }
  108. [self
  109. runAfterRetryValueFromResponse:response
  110. attempts:tries
  111. block:^{
  112. [self
  113. startDataTaskWithRequest:
  114. request
  115. retryLimit:
  116. retryLimit
  117. tries:
  118. (tries +
  119. 1)
  120. completionHandler:
  121. completionHandler];
  122. }];
  123. }];
  124. }];
  125. [task resume];
  126. if (!task) {
  127. completionHandler(nil, nil,
  128. [NSError errorWithDomain:FIRCLSNetworkErrorDomain
  129. code:FIRCLSNetworkErrorFailedToStartOperation
  130. userInfo:nil]);
  131. }
  132. }
  133. - (void)startDataTaskWithRequest:(NSURLRequest *)request
  134. retryLimit:(NSUInteger)retryLimit
  135. completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler {
  136. [self startDataTaskWithRequest:request
  137. retryLimit:retryLimit
  138. tries:0
  139. completionHandler:completionHandler];
  140. }
  141. - (void)startDataTaskWithRequest:(NSURLRequest *)request
  142. completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler {
  143. [self startDataTaskWithRequest:request
  144. retryLimit:FIRCLSNetworkMaximumRetryCount
  145. completionHandler:completionHandler];
  146. }
  147. - (void)startDownloadTaskWithRequest:(NSURLRequest *)request
  148. retryLimit:(NSUInteger)retryLimit
  149. tries:(NSUInteger)tries
  150. completionHandler:
  151. (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler {
  152. NSURLSessionTask *task = [self.session
  153. downloadTaskWithRequest:request
  154. completionHandler:^(NSURL *location, NSURLResponse *response, NSError *taskError) {
  155. [FIRCLSNetworkResponseHandler
  156. handleCompletedResponse:response
  157. forOriginalRequest:request
  158. error:taskError
  159. block:^(BOOL retry, NSError *error) {
  160. if (!retry) {
  161. completionHandler(location, response, error);
  162. return;
  163. }
  164. if (tries >= retryLimit) {
  165. NSDictionary *userInfo = @{
  166. @"retryLimit" : @(retryLimit),
  167. NSURLErrorFailingURLStringErrorKey : request.URL
  168. };
  169. completionHandler(
  170. nil, nil,
  171. [NSError
  172. errorWithDomain:FIRCLSNetworkErrorDomain
  173. code:
  174. FIRCLSNetworkErrorMaximumAttemptsReached
  175. userInfo:userInfo]);
  176. return;
  177. }
  178. [self
  179. runAfterRetryValueFromResponse:response
  180. attempts:tries
  181. block:^{
  182. [self
  183. startDownloadTaskWithRequest:
  184. request
  185. retryLimit:
  186. retryLimit
  187. tries:
  188. (tries +
  189. 1)
  190. completionHandler:
  191. completionHandler];
  192. }];
  193. }];
  194. }];
  195. [task resume];
  196. if (!task) {
  197. completionHandler(nil, nil,
  198. [NSError errorWithDomain:FIRCLSNetworkErrorDomain
  199. code:FIRCLSNetworkErrorFailedToStartOperation
  200. userInfo:nil]);
  201. }
  202. }
  203. - (void)startDownloadTaskWithRequest:(NSURLRequest *)request
  204. retryLimit:(NSUInteger)retryLimit
  205. completionHandler:
  206. (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler {
  207. [self startDownloadTaskWithRequest:request
  208. retryLimit:retryLimit
  209. tries:0
  210. completionHandler:completionHandler];
  211. }
  212. - (void)startDownloadTaskWithRequest:(NSURLRequest *)request
  213. completionHandler:
  214. (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler {
  215. [self startDownloadTaskWithRequest:request
  216. retryLimit:FIRCLSNetworkMaximumRetryCount
  217. completionHandler:completionHandler];
  218. }
  219. - (void)invalidateAndCancel {
  220. [self.session invalidateAndCancel];
  221. }
  222. #pragma mark - NSURLSession Delegate
  223. - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
  224. }
  225. @end