| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- // Copyright 2019 Google
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #import "Crashlytics/Shared/FIRCLSNetworking/FIRCLSFABNetworkClient.h"
- #import "Crashlytics/Shared/FIRCLSNetworking/FIRCLSNetworkResponseHandler.h"
- static const float FIRCLSNetworkMinimumRetryJitter = 0.90f;
- static const float FIRCLSNetworkMaximumRetryJitter = 1.10f;
- const NSUInteger FIRCLSNetworkMaximumRetryCount = 10;
- @interface FIRCLSFABNetworkClient () <NSURLSessionDelegate, NSURLSessionTaskDelegate>
- @property(nonatomic, strong, readonly) NSURLSession *session;
- @end
- @implementation FIRCLSFABNetworkClient
- - (instancetype)init {
- return [self initWithQueue:nil];
- }
- - (instancetype)initWithQueue:(nullable NSOperationQueue *)operationQueue {
- NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
- return [self initWithSessionConfiguration:config queue:operationQueue];
- }
- - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)config
- queue:(nullable NSOperationQueue *)operationQueue {
- self = [super init];
- if (!self) {
- return nil;
- }
- _session = [NSURLSession sessionWithConfiguration:config
- delegate:self
- delegateQueue:operationQueue];
- if (!_session) {
- return nil;
- }
- return self;
- }
- - (void)dealloc {
- [_session finishTasksAndInvalidate];
- }
- #pragma mark - Delay Handling
- - (double)randomDoubleWithMin:(double)min max:(double)max {
- return min + ((max - min) * drand48());
- }
- - (double)generateRandomJitter {
- return [self randomDoubleWithMin:FIRCLSNetworkMinimumRetryJitter
- max:FIRCLSNetworkMaximumRetryJitter];
- }
- - (NSTimeInterval)computeDelayForResponse:(NSURLResponse *)response
- withRetryCount:(NSUInteger)count {
- NSTimeInterval initialValue = [FIRCLSNetworkResponseHandler retryValueForResponse:response];
- // make sure count is > 0
- count = MAX(count, 1);
- // make sure initialValue is >2 for exponential backoff to work reasonably with low count numbers
- initialValue = MAX(initialValue, 2.0);
- const double jitter = [self generateRandomJitter];
- return pow(initialValue, count) * jitter; // exponential backoff
- }
- - (void)runAfterRetryValueFromResponse:(NSURLResponse *)response
- attempts:(NSUInteger)count
- onQueue:(dispatch_queue_t)queue
- block:(void (^)(void))block {
- const NSTimeInterval delay = [self computeDelayForResponse:response withRetryCount:count];
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(delay * NSEC_PER_SEC)), queue, block);
- }
- - (void)runAfterRetryValueFromResponse:(NSURLResponse *)response
- attempts:(NSUInteger)count
- block:(void (^)(void))block {
- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- [self runAfterRetryValueFromResponse:response attempts:count onQueue:queue block:block];
- }
- #pragma mark - Tasks
- - (void)startDataTaskWithRequest:(NSURLRequest *)request
- retryLimit:(NSUInteger)retryLimit
- tries:(NSUInteger)tries
- completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler {
- NSURLSessionTask *task = [self.session
- dataTaskWithRequest:request
- completionHandler:^(NSData *data, NSURLResponse *response, NSError *taskError) {
- [FIRCLSNetworkResponseHandler
- handleCompletedResponse:response
- forOriginalRequest:request
- error:taskError
- block:^(BOOL retry, NSError *error) {
- if (!retry) {
- completionHandler(data, response, error);
- return;
- }
- if (tries >= retryLimit) {
- NSDictionary *userInfo = @{
- @"retryLimit" : @(retryLimit),
- NSURLErrorFailingURLStringErrorKey : request.URL
- };
- completionHandler(
- nil, nil,
- [NSError
- errorWithDomain:FIRCLSNetworkErrorDomain
- code:FIRCLSNetworkErrorMaximumAttemptsReached
- userInfo:userInfo]);
- return;
- }
- [self
- runAfterRetryValueFromResponse:response
- attempts:tries
- block:^{
- [self
- startDataTaskWithRequest:
- request
- retryLimit:
- retryLimit
- tries:
- (tries +
- 1)
- completionHandler:
- completionHandler];
- }];
- }];
- }];
- [task resume];
- if (!task) {
- completionHandler(nil, nil,
- [NSError errorWithDomain:FIRCLSNetworkErrorDomain
- code:FIRCLSNetworkErrorFailedToStartOperation
- userInfo:nil]);
- }
- }
- - (void)startDataTaskWithRequest:(NSURLRequest *)request
- retryLimit:(NSUInteger)retryLimit
- completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler {
- [self startDataTaskWithRequest:request
- retryLimit:retryLimit
- tries:0
- completionHandler:completionHandler];
- }
- - (void)startDataTaskWithRequest:(NSURLRequest *)request
- completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler {
- [self startDataTaskWithRequest:request
- retryLimit:FIRCLSNetworkMaximumRetryCount
- completionHandler:completionHandler];
- }
- - (void)startDownloadTaskWithRequest:(NSURLRequest *)request
- retryLimit:(NSUInteger)retryLimit
- tries:(NSUInteger)tries
- completionHandler:
- (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler {
- NSURLSessionTask *task = [self.session
- downloadTaskWithRequest:request
- completionHandler:^(NSURL *location, NSURLResponse *response, NSError *taskError) {
- [FIRCLSNetworkResponseHandler
- handleCompletedResponse:response
- forOriginalRequest:request
- error:taskError
- block:^(BOOL retry, NSError *error) {
- if (!retry) {
- completionHandler(location, response, error);
- return;
- }
- if (tries >= retryLimit) {
- NSDictionary *userInfo = @{
- @"retryLimit" : @(retryLimit),
- NSURLErrorFailingURLStringErrorKey : request.URL
- };
- completionHandler(
- nil, nil,
- [NSError
- errorWithDomain:FIRCLSNetworkErrorDomain
- code:
- FIRCLSNetworkErrorMaximumAttemptsReached
- userInfo:userInfo]);
- return;
- }
- [self
- runAfterRetryValueFromResponse:response
- attempts:tries
- block:^{
- [self
- startDownloadTaskWithRequest:
- request
- retryLimit:
- retryLimit
- tries:
- (tries +
- 1)
- completionHandler:
- completionHandler];
- }];
- }];
- }];
- [task resume];
- if (!task) {
- completionHandler(nil, nil,
- [NSError errorWithDomain:FIRCLSNetworkErrorDomain
- code:FIRCLSNetworkErrorFailedToStartOperation
- userInfo:nil]);
- }
- }
- - (void)startDownloadTaskWithRequest:(NSURLRequest *)request
- retryLimit:(NSUInteger)retryLimit
- completionHandler:
- (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler {
- [self startDownloadTaskWithRequest:request
- retryLimit:retryLimit
- tries:0
- completionHandler:completionHandler];
- }
- - (void)startDownloadTaskWithRequest:(NSURLRequest *)request
- completionHandler:
- (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler {
- [self startDownloadTaskWithRequest:request
- retryLimit:FIRCLSNetworkMaximumRetryCount
- completionHandler:completionHandler];
- }
- - (void)invalidateAndCancel {
- [self.session invalidateAndCancel];
- }
- #pragma mark - NSURLSession Delegate
- - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
- }
- @end
|