Added the feature to config the status code and content type checking logic, do not hard-coded 200-400

This commit is contained in:
DreamPiggy 2021-05-07 16:01:31 +08:00
parent 76dd4b4911
commit df7d56373d
7 changed files with 74 additions and 6 deletions

View File

@ -349,6 +349,14 @@ static void * SDWebImageDownloaderContext = &SDWebImageDownloaderContext;
operation.minimumProgressInterval = MIN(MAX(self.config.minimumProgressInterval, 0), 1); operation.minimumProgressInterval = MIN(MAX(self.config.minimumProgressInterval, 0), 1);
} }
if ([operation respondsToSelector:@selector(setAcceptableStatusCodes:)]) {
operation.acceptableStatusCodes = self.config.acceptableStatusCodes;
}
if ([operation respondsToSelector:@selector(setAcceptableContentTypes:)]) {
operation.acceptableContentTypes = self.config.acceptableContentTypes;
}
if (options & SDWebImageDownloaderHighPriority) { if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh; operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) { } else if (options & SDWebImageDownloaderLowPriority) {

View File

@ -95,4 +95,19 @@ typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
*/ */
@property (nonatomic, copy, nullable) NSString *password; @property (nonatomic, copy, nullable) NSString *password;
/**
* Set the acceptable HTTP Response status code. The status code which beyond the range will mark the download operation failed.
* For example, if we config [200, 400) but server response is 503, the download will fail with error code `SDWebImageErrorInvalidDownloadStatusCode`.
* Defaults to [200,400). Nil means no validation at all.
*/
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;
/**
* Set the acceptable HTTP Response content type. The content type beyond the set will mark the download operation failed.
* For example, if we config ["image/png"] but server response is "application/json", the download will fail with error code `SDWebImageErrorInvalidDownloadContentType`.
* Normally you don't need this for image format detection because we use image's data file signature magic bytes: https://en.wikipedia.org/wiki/List_of_file_signatures
* Defaults to nil. Nil means no validation at all.
*/
@property (nonatomic, copy, nullable) NSSet<NSString *> *acceptableContentTypes;
@end @end

View File

@ -26,6 +26,7 @@ static SDWebImageDownloaderConfig * _defaultDownloaderConfig;
_maxConcurrentDownloads = 6; _maxConcurrentDownloads = 6;
_downloadTimeout = 15.0; _downloadTimeout = 15.0;
_executionOrder = SDWebImageDownloaderFIFOExecutionOrder; _executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
_acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
} }
return self; return self;
} }
@ -41,6 +42,8 @@ static SDWebImageDownloaderConfig * _defaultDownloaderConfig;
config.urlCredential = self.urlCredential; config.urlCredential = self.urlCredential;
config.username = self.username; config.username = self.username;
config.password = self.password; config.password = self.password;
config.acceptableStatusCodes = self.acceptableStatusCodes;
config.acceptableContentTypes = self.acceptableContentTypes;
return config; return config;
} }

View File

@ -37,8 +37,12 @@
@optional @optional
@property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask; @property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask;
@property (strong, nonatomic, readonly, nullable) NSURLSessionTaskMetrics *metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); @property (strong, nonatomic, readonly, nullable) NSURLSessionTaskMetrics *metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
// These operation-level config was inherited from downloader. See `SDWebImageDownloaderConfig` for documentation.
@property (strong, nonatomic, nullable) NSURLCredential *credential; @property (strong, nonatomic, nullable) NSURLCredential *credential;
@property (assign, nonatomic) double minimumProgressInterval; @property (assign, nonatomic) double minimumProgressInterval;
@property (copy, nonatomic, nullable) NSIndexSet *acceptableStatusCodes;
@property (copy, nonatomic, nullable) NSSet<NSString *> *acceptableContentTypes;
@end @end
@ -85,6 +89,21 @@
*/ */
@property (assign, nonatomic) double minimumProgressInterval; @property (assign, nonatomic) double minimumProgressInterval;
/**
* Set the acceptable HTTP Response status code. The status code which beyond the range will mark the download operation failed.
* For example, if we config [200, 400) but server response is 503, the download will fail with error code `SDWebImageErrorInvalidDownloadStatusCode`.
* Defaults to [200,400). Nil means no validation at all.
*/
@property (copy, nonatomic, nullable) NSIndexSet *acceptableStatusCodes;
/**
* Set the acceptable HTTP Response content type. The content type beyond the set will mark the download operation failed.
* For example, if we config ["image/png"] but server response is "application/json", the download will fail with error code `SDWebImageErrorInvalidDownloadContentType`.
* Normally you don't need this for image format detection because we use image's data file signature magic bytes: https://en.wikipedia.org/wiki/List_of_file_signatures
* Defaults to nil. Nil means no validation at all.
*/
@property (copy, nonatomic, nullable) NSSet<NSString *> *acceptableContentTypes;
/** /**
* The options for the receiver. * The options for the receiver.
*/ */

View File

@ -332,18 +332,37 @@ didReceiveResponse:(NSURLResponse *)response
self.expectedSize = expected; self.expectedSize = expected;
self.response = response; self.response = response;
NSInteger statusCode = [response respondsToSelector:@selector(statusCode)] ? ((NSHTTPURLResponse *)response).statusCode : 200; // Check status code valid (defaults [200,400))
// Status code should between [200,400) NSInteger statusCode = [response isKindOfClass:NSHTTPURLResponse.class] ? ((NSHTTPURLResponse *)response).statusCode : 0;
BOOL statusCodeValid = statusCode >= 200 && statusCode < 400; BOOL statusCodeValid = YES;
if (valid && statusCode > 0 && self.acceptableStatusCodes) {
statusCodeValid = [self.acceptableStatusCodes containsIndex:statusCode];
}
if (!statusCodeValid) { if (!statusCodeValid) {
valid = NO; valid = NO;
self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidDownloadStatusCode userInfo:@{NSLocalizedDescriptionKey : @"Download marked as failed because response status code is not in 200-400", SDWebImageErrorDownloadStatusCodeKey : @(statusCode)}]; self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain
code:SDWebImageErrorInvalidDownloadStatusCode
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Download marked as failed because of invalid response status code %ld", (long)statusCode], SDWebImageErrorDownloadStatusCodeKey: @(statusCode)}];
}
// Check content type valid (defaults nil)
NSString *contentType = [response isKindOfClass:NSHTTPURLResponse.class] ? ((NSHTTPURLResponse *)response).MIMEType : nil;
BOOL contentTypeValid = YES;
if (valid && contentType.length > 0 && self.acceptableContentTypes) {
contentTypeValid = [self.acceptableContentTypes containsObject:contentType];
}
if (!contentTypeValid) {
valid = NO;
self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain
code:SDWebImageErrorInvalidDownloadContentType
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Download marked as failed because of invalid response content type %@", contentType], SDWebImageErrorDownloadContentTypeKey: contentType}];
} }
//'304 Not Modified' is an exceptional one //'304 Not Modified' is an exceptional one
//URLSession current behavior will return 200 status code when the server respond 304 and URLCache hit. But this is not a standard behavior and we just add a check //URLSession current behavior will return 200 status code when the server respond 304 and URLCache hit. But this is not a standard behavior and we just add a check
if (statusCode == 304 && !self.cachedData) { if (valid && statusCode == 304 && !self.cachedData) {
valid = NO; valid = NO;
self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCacheNotModified userInfo:@{NSLocalizedDescriptionKey : @"Download response status code is 304 not modified and ignored"}]; self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain
code:SDWebImageErrorCacheNotModified
userInfo:@{NSLocalizedDescriptionKey: @"Download response status code is 304 not modified and ignored"}];
} }
if (valid) { if (valid) {

View File

@ -13,6 +13,8 @@ FOUNDATION_EXPORT NSErrorDomain const _Nonnull SDWebImageErrorDomain;
/// The HTTP status code for invalid download response (NSNumber *) /// The HTTP status code for invalid download response (NSNumber *)
FOUNDATION_EXPORT NSErrorUserInfoKey const _Nonnull SDWebImageErrorDownloadStatusCodeKey; FOUNDATION_EXPORT NSErrorUserInfoKey const _Nonnull SDWebImageErrorDownloadStatusCodeKey;
/// The HTTP MIME content type for invalid download response (NSString *)
FOUNDATION_EXPORT NSErrorUserInfoKey const _Nonnull SDWebImageErrorDownloadContentTypeKey;
/// SDWebImage error domain and codes /// SDWebImage error domain and codes
typedef NS_ERROR_ENUM(SDWebImageErrorDomain, SDWebImageError) { typedef NS_ERROR_ENUM(SDWebImageErrorDomain, SDWebImageError) {
@ -24,4 +26,5 @@ typedef NS_ERROR_ENUM(SDWebImageErrorDomain, SDWebImageError) {
SDWebImageErrorInvalidDownloadStatusCode = 2001, // The image download response a invalid status code. You can check the status code in error's userInfo under `SDWebImageErrorDownloadStatusCodeKey` SDWebImageErrorInvalidDownloadStatusCode = 2001, // The image download response a invalid status code. You can check the status code in error's userInfo under `SDWebImageErrorDownloadStatusCodeKey`
SDWebImageErrorCancelled = 2002, // The image loading operation is cancelled before finished, during either async disk cache query, or waiting before actual network request. For actual network request error, check `NSURLErrorDomain` error domain and code. SDWebImageErrorCancelled = 2002, // The image loading operation is cancelled before finished, during either async disk cache query, or waiting before actual network request. For actual network request error, check `NSURLErrorDomain` error domain and code.
SDWebImageErrorInvalidDownloadResponse = 2003, // When using response modifier, the modified download response is nil and marked as failed. SDWebImageErrorInvalidDownloadResponse = 2003, // When using response modifier, the modified download response is nil and marked as failed.
SDWebImageErrorInvalidDownloadContentType = 2004, // The image download response a invalid content type. You can check the MIME content type in error's userInfo under `SDWebImageErrorDownloadContentTypeKey`
}; };

View File

@ -11,3 +11,4 @@
NSErrorDomain const _Nonnull SDWebImageErrorDomain = @"SDWebImageErrorDomain"; NSErrorDomain const _Nonnull SDWebImageErrorDomain = @"SDWebImageErrorDomain";
NSErrorUserInfoKey const _Nonnull SDWebImageErrorDownloadStatusCodeKey = @"SDWebImageErrorDownloadStatusCodeKey"; NSErrorUserInfoKey const _Nonnull SDWebImageErrorDownloadStatusCodeKey = @"SDWebImageErrorDownloadStatusCodeKey";
NSErrorUserInfoKey const _Nonnull SDWebImageErrorDownloadContentTypeKey = @"SDWebImageErrorDownloadContentTypeKey";