Add support for retrying and reduce default timeout. (#215)

* Add support for retrying and reduce default timeout.

* Return HTTP error codes >= 400. Don't retry when failure is sure to occur again.

* Add missing nullability

* Use NSInteger
This commit is contained in:
Garrett Moon
2016-07-15 16:18:58 -07:00
committed by GitHub
parent c9f3544c4e
commit ded2e07dd3
6 changed files with 64 additions and 13 deletions

View File

@@ -284,9 +284,8 @@
{
NSError *outError = result.error;
XCTAssert([outError.domain isEqualToString:NSURLErrorDomain]);
XCTAssert(outError.code == NSURLErrorRedirectToNonExistentLocation);
XCTAssert([outError.localizedDescription isEqualToString:@"The requested URL was not found on this server."]);
XCTAssert([outError.domain isEqualToString:PINURLErrorDomain]);
XCTAssert(outError.code == 404);
[expectation fulfill];
}];

View File

@@ -18,6 +18,8 @@
@property (nonatomic, assign) BOOL hasProgressBlocks;
@property (nonatomic, strong, nullable) PINProgressiveImage *progressImage;
@property (nonatomic, assign) NSUInteger numberOfRetries;
- (void)callProgressDownloadWithQueue:(nonnull dispatch_queue_t)queue completedBytes:(int64_t)completedBytes totalBytes:(int64_t)totalBytes;
- (void)callProgressImageWithQueue:(nonnull dispatch_queue_t)queue withImage:(nonnull PINImage *)image renderedImageQuality:(CGFloat)renderedImageQuality;

View File

@@ -23,6 +23,7 @@
{
if (self = [super init]) {
_canSetDataTaskPriority = [NSURLSessionTask instancesRespondToSelector:@selector(setPriority:)];
_numberOfRetries = 0;
}
return self;
}

View File

@@ -26,7 +26,9 @@
#import "NSData+ImageDetectors.h"
#import "PINImage+DecodedImage.h"
#define PINRemoteImageManagerDefaultTimeout 60.0
#define PINRemoteImageManagerDefaultTimeout 30.0
#define PINRemoteImageMaxRetries 3
#define PINRemoteImageRetryDelayBase 4
//A limit of 200 characters is chosen because PINDiskCache
//may expand the length by encoding certain characters
@@ -696,7 +698,7 @@ static dispatch_once_t sharedDispatchToken;
{
[self lock];
PINRemoteImageDownloadTask *task = [self.tasks objectForKey:key];
if (task.urlSessionTaskOperation == nil && task.callbackBlocks.count > 0) {
if (task.urlSessionTaskOperation == nil && task.callbackBlocks.count > 0 && task.numberOfRetries == 0) {
//If completionBlocks.count == 0, we've canceled before we were even able to start.
CFTimeInterval startTime = CACurrentMediaTime();
PINDataTaskOperation *urlSessionTaskOperation = [self sessionTaskWithURL:url key:key options:options priority:priority];
@@ -750,9 +752,9 @@ static dispatch_once_t sharedDispatchToken;
}
- (PINDataTaskOperation *)sessionTaskWithURL:(NSURL *)URL
key:(NSString *)key
options:(PINRemoteImageManagerDownloadOptions)options
priority:(PINRemoteImageManagerPriority)priority
key:(NSString *)key
options:(PINRemoteImageManagerDownloadOptions)options
priority:(PINRemoteImageManagerPriority)priority
{
__weak typeof(self) weakSelf = self;
return [self downloadDataWithURL:URL
@@ -767,7 +769,36 @@ static dispatch_once_t sharedDispatchToken;
PINImage *image = nil;
id alternativeRepresentation = nil;
if (remoteImageError == nil) {
if (remoteImageError && [[self class] retriableError:remoteImageError]) {
//attempt to retry after delay
BOOL retry = NO;
NSUInteger newNumberOfRetries = 0;
[strongSelf lock];
PINRemoteImageDownloadTask *task = [self.tasks objectForKey:key];
if (task.numberOfRetries < PINRemoteImageMaxRetries) {
retry = YES;
newNumberOfRetries = ++task.numberOfRetries;
task.urlSessionTaskOperation = nil;
}
[strongSelf unlock];
if (retry) {
int64_t delay = powf(PINRemoteImageRetryDelayBase, newNumberOfRetries);
PINLog(@"Retrying download of %@ in %d seconds.", URL, delay);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
typeof(self) strongSelf = weakSelf;
[strongSelf lock];
PINRemoteImageDownloadTask *task = [strongSelf.tasks objectForKey:key];
if (task.urlSessionTaskOperation == nil && task.callbackBlocks.count > 0) {
//If completionBlocks.count == 0, we've canceled before we were even able to start.
PINDataTaskOperation *urlSessionTaskOperation = [strongSelf sessionTaskWithURL:URL key:key options:options priority:priority];
task.urlSessionTaskOperation = urlSessionTaskOperation;
}
[strongSelf unlock];
});
return;
}
} else if (remoteImageError == nil) {
//stores the object in the caches
[strongSelf materializeAndCacheObject:data cacheInDisk:data additionalCost:0 key:key options:options outImage:&image outAltRep:&alternativeRepresentation];
}
@@ -783,6 +814,17 @@ static dispatch_once_t sharedDispatchToken;
}];
}
+ (BOOL)retriableError:(NSError *)remoteImageError
{
if ([remoteImageError.domain isEqualToString:PINURLErrorDomain]) {
return remoteImageError.code >= 500;
} else if ([remoteImageError.domain isEqualToString:NSURLErrorDomain] && remoteImageError.code == NSURLErrorUnsupportedURL) {
return NO;
} else if ([remoteImageError.domain isEqualToString:PINRemoteImageManagerErrorDomain]) {
return NO;
}
return YES;
}
- (PINDataTaskOperation *)downloadDataWithURL:(NSURL *)url
key:(NSString *)key

View File

@@ -8,6 +8,8 @@
#import <Foundation/Foundation.h>
extern NSString * __nonnull const PINURLErrorDomain;
@protocol PINURLSessionManagerDelegate <NSObject>
@required

View File

@@ -8,6 +8,8 @@
#import "PINURLSessionManager.h"
NSString * const PINURLErrorDomain = @"PINURLErrorDomain";
@interface PINURLSessionManager () <NSURLSessionDelegate, NSURLSessionDataDelegate>
@property (nonatomic, strong) NSLock *sessionManagerLock;
@@ -114,10 +116,13 @@
[self lock];
dispatch_queue_t delegateQueue = self.delegateQueues[@(task.taskIdentifier)];
[self unlock];
if (!error && [task.response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)task.response statusCode] == 404) {
error = [NSError errorWithDomain:NSURLErrorDomain
code:NSURLErrorRedirectToNonExistentLocation
userInfo:@{NSLocalizedDescriptionKey : @"The requested URL was not found on this server."}];
if (!error && [task.response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *)task.response statusCode];
if (statusCode >= 400) {
error = [NSError errorWithDomain:PINURLErrorDomain
code:statusCode
userInfo:@{NSLocalizedDescriptionKey : @"HTTP Error Response."}];
}
}
__weak typeof(self) weakSelf = self;
dispatch_async(delegateQueue, ^{