Refactory to seperate the response modifier and data decryptor, one for HTTP response, one for Data decrypt.

This commit is contained in:
DreamPiggy 2019-10-15 20:10:00 +08:00
parent 9b6d2b5691
commit 34e736375e
12 changed files with 228 additions and 99 deletions

View File

@ -60,6 +60,10 @@
324DF4B6200A14DC008A84CC /* SDWebImageDefine.h in Headers */ = {isa = PBXBuildFile; fileRef = 324DF4B2200A14DC008A84CC /* SDWebImageDefine.h */; settings = {ATTRIBUTES = (Public, ); }; };
324DF4BA200A14DC008A84CC /* SDWebImageDefine.m in Sources */ = {isa = PBXBuildFile; fileRef = 324DF4B3200A14DC008A84CC /* SDWebImageDefine.m */; };
324DF4BC200A14DC008A84CC /* SDWebImageDefine.m in Sources */ = {isa = PBXBuildFile; fileRef = 324DF4B3200A14DC008A84CC /* SDWebImageDefine.m */; };
3250C9EE2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 3250C9EC2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h */; settings = {ATTRIBUTES = (Public, ); }; };
3250C9EF2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3250C9ED2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m */; };
3250C9F02355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3250C9ED2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m */; };
3250C9F12355E3DF0093A896 /* SDWebImageDownloaderDecryptor.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3250C9EC2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h */; };
325312CA200F09910046BF1E /* SDWebImageTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = 325312C6200F09910046BF1E /* SDWebImageTransition.h */; settings = {ATTRIBUTES = (Public, ); }; };
325312CE200F09910046BF1E /* SDWebImageTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 325312C7200F09910046BF1E /* SDWebImageTransition.m */; };
325312D0200F09910046BF1E /* SDWebImageTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 325312C7200F09910046BF1E /* SDWebImageTransition.m */; };
@ -284,6 +288,7 @@
dstPath = include/SDWebImage;
dstSubfolderSpec = 16;
files = (
3250C9F12355E3DF0093A896 /* SDWebImageDownloaderDecryptor.h in Copy Headers */,
325427662355783C0042BAA4 /* SDWebImageDownloaderResponseModifier.h in Copy Headers */,
3298655F233723220071958B /* SDImageHEICCoder.h in Copy Headers */,
32C78E3823336FC800C6B7F8 /* SDImageIOAnimatedCoder.h in Copy Headers */,
@ -373,6 +378,8 @@
3248475C201775F600AF9E5A /* SDAnimatedImageView+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "SDAnimatedImageView+WebCache.m"; path = "Core/SDAnimatedImageView+WebCache.m"; sourceTree = "<group>"; };
324DF4B2200A14DC008A84CC /* SDWebImageDefine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageDefine.h; path = Core/SDWebImageDefine.h; sourceTree = "<group>"; };
324DF4B3200A14DC008A84CC /* SDWebImageDefine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDefine.m; path = Core/SDWebImageDefine.m; sourceTree = "<group>"; };
3250C9EC2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageDownloaderDecryptor.h; path = Core/SDWebImageDownloaderDecryptor.h; sourceTree = "<group>"; };
3250C9ED2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDownloaderDecryptor.m; path = Core/SDWebImageDownloaderDecryptor.m; sourceTree = "<group>"; };
325312C6200F09910046BF1E /* SDWebImageTransition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageTransition.h; path = Core/SDWebImageTransition.h; sourceTree = "<group>"; };
325312C7200F09910046BF1E /* SDWebImageTransition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDWebImageTransition.m; path = Core/SDWebImageTransition.m; sourceTree = "<group>"; };
32542761235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageDownloaderResponseModifier.h; path = Core/SDWebImageDownloaderResponseModifier.h; sourceTree = "<group>"; };
@ -766,6 +773,8 @@
32F21B5020788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.m */,
32542761235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.h */,
32542762235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.m */,
3250C9EC2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h */,
3250C9ED2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m */,
321B377D2083290D00C0EA77 /* SDImageLoader.h */,
321B377E2083290D00C0EA77 /* SDImageLoader.m */,
321B377F2083290E00C0EA77 /* SDImageLoadersManager.h */,
@ -864,6 +873,7 @@
4A2CAE1B1AB4BB6800B6BC39 /* SDWebImageDownloader.h in Headers */,
3248476B201775F600AF9E5A /* SDAnimatedImageView.h in Headers */,
32D122322080B2EB003685A3 /* SDImageCachesManager.h in Headers */,
3250C9EE2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h in Headers */,
32F7C0862030719600873181 /* UIImage+Transform.h in Headers */,
321E60C01F38E91700405457 /* UIImage+ForceDecode.h in Headers */,
329F1243223FAD3400B309FD /* SDInternalMacros.h in Headers */,
@ -1064,6 +1074,7 @@
325C46232233A02E004CAE11 /* UIColor+HexString.m in Sources */,
321E60C61F38E91700405457 /* UIImage+ForceDecode.m in Sources */,
3244062E2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */,
3250C9F02355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */,
328BB6A42081FED200760D6C /* SDWebImageCacheKeyFilter.m in Sources */,
4A2CAE2E1AB4BB7500B6BC39 /* UIImage+GIF.m in Sources */,
80B6DF822142B44400BCB334 /* NSButton+WebCache.m in Sources */,
@ -1130,6 +1141,7 @@
325C46222233A02E004CAE11 /* UIColor+HexString.m in Sources */,
321E60C41F38E91700405457 /* UIImage+ForceDecode.m in Sources */,
3244062D2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */,
3250C9EF2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */,
328BB6A22081FED200760D6C /* SDWebImageCacheKeyFilter.m in Sources */,
53761309155AD0D5005750A4 /* SDImageCache.m in Sources */,
80B6DF832142B44500BCB334 /* NSButton+WebCache.m in Sources */,

View File

@ -234,10 +234,15 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextAnimat
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextDownloadRequestModifier;
/**
A id<SDWebImageDownloaderResponseModifier> instance to modify the image download response and download data. It's used for downloader to modify the original response and data from URL and options. This can be used for image data decryption, such as Base64 encoded image. If you provide one, it will ignore the `responseModifier` in downloader and use provided one instead. (id<SDWebImageDownloaderResponseModifier>)
A id<SDWebImageDownloaderResponseModifier> instance to modify the image download response. It's used for downloader to modify the original response from URL and options. If you provide one, it will ignore the `responseModifier` in downloader and use provided one instead. (id<SDWebImageDownloaderResponseModifier>)
*/
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextDownloadResponseModifier;
/**
A id<SDWebImageContextDownloadDecryptor> instance to decrypt the image download data. This can be used for image data decryption, such as Base64 encoded image. If you provide one, it will ignore the `decryptor` in downloader and use provided one instead. (id<SDWebImageContextDownloadDecryptor>)
*/
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextDownloadDecryptor;
/**
A id<SDWebImageCacheKeyFilter> instance to convert an URL into a cache key. It's used when manager need cache key to use image cache. If you provide one, it will ignore the `cacheKeyFilter` in manager and use provided one instead. (id<SDWebImageCacheKeyFilter>)
*/

View File

@ -127,5 +127,6 @@ SDWebImageContextOption const SDWebImageContextOriginalStoreCacheType = @"origin
SDWebImageContextOption const SDWebImageContextAnimatedImageClass = @"animatedImageClass";
SDWebImageContextOption const SDWebImageContextDownloadRequestModifier = @"downloadRequestModifier";
SDWebImageContextOption const SDWebImageContextDownloadResponseModifier = @"downloadResponseModifier";
SDWebImageContextOption const SDWebImageContextDownloadDecryptor = @"downloadDecryptor";
SDWebImageContextOption const SDWebImageContextCacheKeyFilter = @"cacheKeyFilter";
SDWebImageContextOption const SDWebImageContextCacheSerializer = @"cacheSerializer";

View File

@ -13,6 +13,7 @@
#import "SDWebImageDownloaderConfig.h"
#import "SDWebImageDownloaderRequestModifier.h"
#import "SDWebImageDownloaderResponseModifier.h"
#import "SDWebImageDownloaderDecryptor.h"
#import "SDImageLoader.h"
/// Downloader options
@ -150,13 +151,22 @@ typedef SDImageLoaderCompletedBlock SDWebImageDownloaderCompletedBlock;
@property (nonatomic, strong, nullable) id<SDWebImageDownloaderRequestModifier> requestModifier;
/**
* Set the response modifier to modify the original download response or download data before decoding. This can be used for image data decryption, such as Base64 encoded image.
* This request modifier method will be called for each downloading image response. Return the original response or data means no modification. Return nil from either response or data, will mark this download failed.
* Defaults to nil, means does not modify the original download response or download data.
* Set the response modifier to modify the original download response during image load.
* This request modifier method will be called for each downloading image response. Return the original response means no modification. Return nil will mark current download as cancelled.
* Defaults to nil, means does not modify the original download response.
* @note If you want to modify single response, consider using `SDWebImageContextDownloadResponseModifier` context option.
*/
@property (nonatomic, strong, nullable) id<SDWebImageDownloaderResponseModifier> responseModifier;
/**
* Set the decryptor to decrypt the original download data before image decoding. This can be used for encrypted image data, like Base64.
* This decryptor method will be called for each downloading image data. Return the original data means no modification. Return nil will mark this download failed.
* Defaults to nil, means does not modify the original download data.
* @note When using decryptor, progressive decoding will be disabled, to avoid data corrupt issue.
* @note If you want to decrypt single download data, consider using `SDWebImageContextDownloadDecryptor` context option.
*/
@property (nonatomic, strong, nullable) id<SDWebImageDownloaderDecryptor> decryptor;
/**
* The configuration in use by the internal NSURLSession. If you want to provide a custom sessionConfiguration, use `SDWebImageDownloaderConfig.sessionConfiguration` and create a new downloader instance.
@note This is immutable according to NSURLSession's documentation. Mutating this object directly has no effect.

View File

@ -272,6 +272,15 @@ static void * SDWebImageDownloaderContext = &SDWebImageDownloaderContext;
SD_LOCK(self.HTTPHeadersLock);
mutableRequest.allHTTPHeaderFields = self.HTTPHeaders;
SD_UNLOCK(self.HTTPHeadersLock);
// Context Option
SDWebImageMutableContext *mutableContext;
if (context) {
mutableContext = [context mutableCopy];
} else {
mutableContext = [NSMutableDictionary dictionary];
}
// Request Modifier
id<SDWebImageDownloaderRequestModifier> requestModifier;
if ([context valueForKey:SDWebImageContextDownloadRequestModifier]) {
@ -299,17 +308,22 @@ static void * SDWebImageDownloaderContext = &SDWebImageDownloaderContext;
} else {
responseModifier = self.responseModifier;
}
if (responseModifier) {
SDWebImageMutableContext *mutableContext;
if (context) {
mutableContext = [context mutableCopy];
} else {
mutableContext = [NSMutableDictionary dictionary];
}
mutableContext[SDWebImageContextDownloadResponseModifier] = responseModifier;
context = [mutableContext copy];
}
// Decryptor
id<SDWebImageDownloaderDecryptor> decryptor;
if ([context valueForKey:SDWebImageContextDownloadDecryptor]) {
decryptor = [context valueForKey:SDWebImageContextDownloadDecryptor];
} else {
decryptor = self.decryptor;
}
if (decryptor) {
mutableContext[SDWebImageContextDownloadDecryptor] = decryptor;
}
context = [mutableContext copy];
// Operation Class
Class operationClass = self.config.operationClass;
if (operationClass && [operationClass isSubclassOfClass:[NSOperation class]] && [operationClass conformsToProtocol:@protocol(SDWebImageDownloaderOperation)]) {

View File

@ -0,0 +1,44 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import <Foundation/Foundation.h>
#import "SDWebImageCompat.h"
typedef NSData * _Nullable (^SDWebImageDownloaderDecryptorBlock)(NSData * _Nonnull data, NSURLResponse * _Nullable response);
/**
This is the protocol for downloader decryptor. Which decrypt the original encrypted data before decoding. Note progressive decoding is not compatible for decryptor.
We can use a block to specify the downloader decryptor. But Using protocol can make this extensible, and allow Swift user to use it easily instead of using `@convention(block)` to store a block into context options.
*/
@protocol SDWebImageDownloaderDecryptor <NSObject>
/// Decrypt the original download data and return a new data. You can use this to decrypt the data using your perfereed algorithm.
/// @param data The original download data
/// @param response The URL response for data. If you modifiy the original URL response via response modifier, the modified version will be here . This arg is nullable.
/// @note If nil is returned, the image download will marked as failed with error `SDWebImageErrorBadImageData`
- (nullable NSData *)decryptedDataWithData:(nonnull NSData *)data response:(nullable NSURLResponse *)response;
@end
/**
A downloader response modifier class with block.
*/
@interface SDWebImageDownloaderDecryptor : NSObject <SDWebImageDownloaderDecryptor>
- (nonnull instancetype)initWithBlock:(nonnull SDWebImageDownloaderDecryptorBlock)block;
+ (nonnull instancetype)decryptorWithBlock:(nonnull SDWebImageDownloaderDecryptorBlock)block;
@end
/// Convenience way to create decryptor for common data encryption.
@interface SDWebImageDownloaderDecryptor (Conveniences)
/// Base64 Encoded image data decryptor
@property (class, readonly, nonnull) SDWebImageDownloaderDecryptor *base64Decryptor;
@end

View File

@ -0,0 +1,55 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageDownloaderDecryptor.h"
@interface SDWebImageDownloaderDecryptor ()
@property (nonatomic, copy, nonnull) SDWebImageDownloaderDecryptorBlock block;
@end
@implementation SDWebImageDownloaderDecryptor
- (instancetype)initWithBlock:(SDWebImageDownloaderDecryptorBlock)block {
self = [super init];
if (self) {
self.block = block;
}
return self;
}
+ (instancetype)decryptorWithBlock:(SDWebImageDownloaderDecryptorBlock)block {
SDWebImageDownloaderDecryptor *decryptor = [[SDWebImageDownloaderDecryptor alloc] initWithBlock:block];
return decryptor;
}
- (nullable NSData *)decryptedDataWithData:(nonnull NSData *)data response:(nullable NSURLResponse *)response {
if (!self.block) {
return nil;
}
return self.block(data, response);
}
@end
@implementation SDWebImageDownloaderDecryptor (Conveniences)
+ (SDWebImageDownloaderDecryptor *)base64Decryptor {
static SDWebImageDownloaderDecryptor *decryptor;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
decryptor = [SDWebImageDownloaderDecryptor decryptorWithBlock:^NSData * _Nullable(NSData * _Nonnull data, NSURLResponse * _Nullable response) {
NSData *modifiedData = [[NSData alloc] initWithBase64EncodedData:data options:NSDataBase64DecodingIgnoreUnknownCharacters];
return modifiedData;
}];
});
return decryptor;
}
@end

View File

@ -10,6 +10,7 @@
#import "SDWebImageError.h"
#import "SDInternalMacros.h"
#import "SDWebImageDownloaderResponseModifier.h"
#import "SDWebImageDownloaderDecryptor.h"
// iOS 8 Foundation.framework extern these symbol but the define is in CFNetwork.framework. We just fix this without import CFNetwork.framework
#if ((__IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) || (__MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_11))
@ -38,9 +39,11 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
@property (assign, nonatomic) NSUInteger receivedSize;
@property (strong, nonatomic, nullable, readwrite) NSURLResponse *response;
@property (strong, nonatomic, nullable) NSError *responseError;
@property (strong, nonatomic, nullable) id<SDWebImageDownloaderResponseModifier> responseModifier; // modifiy original URLResponse and Data
@property (assign, nonatomic) double previousProgress; // previous progress percent
@property (strong, nonatomic, nullable) id<SDWebImageDownloaderResponseModifier> responseModifier; // modifiy original URLResponse
@property (strong, nonatomic, nullable) id<SDWebImageDownloaderDecryptor> decryptor; // decrypt image data
// This is weak because it is injected by whoever manages this session. If this gets nil-ed out, we won't be able to run
// the task associated with this operation
@property (weak, nonatomic, nullable) NSURLSession *unownedSession;
@ -77,8 +80,9 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
_request = [request copy];
_options = options;
_context = [context copy];
_responseModifier = context[SDWebImageContextDownloadResponseModifier];
_callbackBlocks = [NSMutableArray new];
_responseModifier = context[SDWebImageContextDownloadResponseModifier];
_decryptor = context[SDWebImageContextDownloadDecryptor];
_executing = NO;
_finished = NO;
_expectedSize = 0;
@ -371,8 +375,8 @@ didReceiveResponse:(NSURLResponse *)response
}
self.previousProgress = currentProgress;
// Response Modifier disable the progressive decoding, no supporting for progressive data modification
BOOL supportProgressive = (self.options & SDWebImageDownloaderProgressiveLoad) && !self.responseModifier;
// Using data decryptor will disable the progressive decoding, since there are no support for progressive decrypt
BOOL supportProgressive = (self.options & SDWebImageDownloaderProgressiveLoad) && !self.decryptor;
if (supportProgressive) {
// Get the image data
NSData *imageData = [self.imageData copy];
@ -440,9 +444,9 @@ didReceiveResponse:(NSURLResponse *)response
if ([self callbacksForKey:kCompletedCallbackKey].count > 0) {
NSData *imageData = [self.imageData copy];
self.imageData = nil;
// response modifier
if (imageData && self.responseModifier) {
imageData = [self.responseModifier modifiedDataWithData:imageData response:self.response];
// data decryptor
if (imageData && self.decryptor) {
imageData = [self.decryptor decryptedDataWithData:imageData response:self.response];
}
if (imageData) {
/** if you specified to only use cached data via `SDWebImageDownloaderIgnoreCachedResponse`,

View File

@ -10,7 +10,6 @@
#import "SDWebImageCompat.h"
typedef NSURLResponse * _Nullable (^SDWebImageDownloaderResponseModifierBlock)(NSURLResponse * _Nonnull response);
typedef NSData * _Nullable (^SDWebImageDownloaderResponseModifierDataBlock)(NSData * _Nonnull data, NSURLResponse * _Nullable response);
/**
This is the protocol for downloader response modifier.
@ -18,12 +17,6 @@ typedef NSData * _Nullable (^SDWebImageDownloaderResponseModifierDataBlock)(NSDa
*/
@protocol SDWebImageDownloaderResponseModifier <NSObject>
/// Modify the original download data and return a new data. You can use this to decrypt the data using your perfereed algorithm.
/// @param data The original download data
/// @param response The URL response for data. If you modifiy the original URL response via the method below, the modified version will be here . This arg is nullable.
/// @note If nil is returned, the image download will marked as failed with error `SDWebImageErrorBadImageData`
- (nullable NSData *)modifiedDataWithData:(nonnull NSData *)data response:(nullable NSURLResponse *)response;
/// Modify the original URL response and return a new response. You can use this to check MIME-Type, mock server response, etc.
/// @param response The original URL response, note for HTTP request it's actually a `NSHTTPURLResponse` instance
/// @note If nil is returned, the image download will marked as cancelled with error `SDWebImageErrorInvalidDownloadResponse`
@ -36,16 +29,7 @@ typedef NSData * _Nullable (^SDWebImageDownloaderResponseModifierDataBlock)(NSDa
*/
@interface SDWebImageDownloaderResponseModifier : NSObject <SDWebImageDownloaderResponseModifier>
- (nonnull instancetype)initWithResponseBlock:(nonnull SDWebImageDownloaderResponseModifierBlock)responseBlock dataBlock:(nonnull SDWebImageDownloaderResponseModifierDataBlock)dataBlock;
+ (nonnull instancetype)responseModifierWithResponseBlock:(nonnull SDWebImageDownloaderResponseModifierBlock)responseBlock dataBlock:(nonnull SDWebImageDownloaderResponseModifierDataBlock)dataBlock;
@end
/// Convenience way to create response modifier for common data encryption.
@interface SDWebImageDownloaderResponseModifier (Conveniences)
/// Base64 Encoded image data decrypter
@property (class, readonly, nonnull) SDWebImageDownloaderResponseModifier *base64ResponseModifier;
- (nonnull instancetype)initWithBlock:(nonnull SDWebImageDownloaderResponseModifierBlock)block;
+ (nonnull instancetype)responseModifierWithBlock:(nonnull SDWebImageDownloaderResponseModifierBlock)block;
@end

View File

@ -11,58 +11,30 @@
@interface SDWebImageDownloaderResponseModifier ()
@property (nonatomic, copy, nonnull) SDWebImageDownloaderResponseModifierBlock responseBlock;
@property (nonatomic, copy, nonnull) SDWebImageDownloaderResponseModifierDataBlock dataBlock;
@property (nonatomic, copy, nonnull) SDWebImageDownloaderResponseModifierBlock block;
@end
@implementation SDWebImageDownloaderResponseModifier
- (instancetype)initWithResponseBlock:(SDWebImageDownloaderResponseModifierBlock)responseBlock dataBlock:(SDWebImageDownloaderResponseModifierDataBlock)dataBlock {
- (instancetype)initWithBlock:(SDWebImageDownloaderResponseModifierBlock)block {
self = [super init];
if (self) {
self.responseBlock = responseBlock;
self.dataBlock = dataBlock;
self.block = block;
}
return self;
}
+ (instancetype)responseModifierWithResponseBlock:(SDWebImageDownloaderResponseModifierBlock)responseBlock dataBlock:(SDWebImageDownloaderResponseModifierDataBlock)dataBlock {
SDWebImageDownloaderResponseModifier *responseModifier = [[SDWebImageDownloaderResponseModifier alloc] initWithResponseBlock:responseBlock dataBlock:dataBlock];
+ (instancetype)responseModifierWithBlock:(SDWebImageDownloaderResponseModifierBlock)block {
SDWebImageDownloaderResponseModifier *responseModifier = [[SDWebImageDownloaderResponseModifier alloc] initWithBlock:block];
return responseModifier;
}
- (nullable NSData *)modifiedDataWithData:(nonnull NSData *)data response:(nullable NSURLResponse *)response {
if (!self.dataBlock) {
return nil;
}
return self.dataBlock(data, response);
}
- (nullable NSURLResponse *)modifiedResponseWithResponse:(nonnull NSURLResponse *)response {
if (!self.responseBlock) {
if (!self.block) {
return nil;
}
return self.responseBlock(response);
}
@end
@implementation SDWebImageDownloaderResponseModifier (Conveniences)
+ (SDWebImageDownloaderResponseModifier *)base64ResponseModifier {
static SDWebImageDownloaderResponseModifier *responseModifier;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
responseModifier = [SDWebImageDownloaderResponseModifier responseModifierWithResponseBlock:^NSURLResponse * _Nullable(NSURLResponse * _Nonnull response) {
return response;
} dataBlock:^NSData * _Nullable(NSData * _Nonnull data, NSURLResponse * _Nullable response) {
NSData *modifiedData = [[NSData alloc] initWithBase64EncodedData:data options:NSDataBase64DecodingIgnoreUnknownCharacters];
return modifiedData;
}];
});
return responseModifier;
return self.block(response);
}
@end

View File

@ -544,13 +544,54 @@
}
- (void)test24ThatDownloadRequestModifierWorks {
XCTestExpectation *expectation1 = [self expectationWithDescription:@"Download response modifier for fileURL"];
XCTestExpectation *expectation2 = [self expectationWithDescription:@"Download response modifier for webURL"];
XCTestExpectation *expectation3 = [self expectationWithDescription:@"Download response modifier invalid response"];
XCTestExpectation *expectation4 = [self expectationWithDescription:@"Download response modifier invalid data"];
XCTestExpectation *expectation1 = [self expectationWithDescription:@"Download response modifier for webURL"];
XCTestExpectation *expectation2 = [self expectationWithDescription:@"Download response modifier invalid response"];
SDWebImageDownloader *downloader = [[SDWebImageDownloader alloc] init];
downloader.responseModifier = SDWebImageDownloaderResponseModifier.base64ResponseModifier;
// 1. Test webURL to response custom status code and header
SDWebImageDownloaderResponseModifier *responseModifier = [SDWebImageDownloaderResponseModifier responseModifierWithBlock:^NSURLResponse * _Nullable(NSURLResponse * _Nonnull response) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSMutableDictionary *mutableHeaderFields = [httpResponse.allHeaderFields mutableCopy];
mutableHeaderFields[@"Foo"] = @"Bar";
NSHTTPURLResponse *modifiedResponse = [[NSHTTPURLResponse alloc] initWithURL:response.URL statusCode:404 HTTPVersion:nil headerFields:[mutableHeaderFields copy]];
return [modifiedResponse copy];
}];
downloader.responseModifier = responseModifier;
__block SDWebImageDownloadToken *token;
token = [downloader downloadImageWithURL:[NSURL URLWithString:kTestJPEGURL] completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
expect(error).notTo.beNil();
expect(error.code).equal(SDWebImageErrorInvalidDownloadStatusCode);
expect(error.userInfo[SDWebImageErrorDownloadStatusCodeKey]).equal(404);
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)token.response;
expect(httpResponse).notTo.beNil();
expect(httpResponse.allHeaderFields[@"Foo"]).equal(@"Bar");
[expectation1 fulfill];
}];
// 2. Test nil response will cancel the download
responseModifier = [SDWebImageDownloaderResponseModifier responseModifierWithBlock:^NSURLResponse * _Nullable(NSURLResponse * _Nonnull response) {
return nil;
}];
[downloader downloadImageWithURL:[NSURL URLWithString:kTestPNGURL] options:0 context:@{SDWebImageContextDownloadResponseModifier : responseModifier} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
expect(error).notTo.beNil();
expect(error.code).equal(SDWebImageErrorInvalidDownloadResponse);
[expectation2 fulfill];
}];
[self waitForExpectationsWithCommonTimeoutUsingHandler:^(NSError * _Nullable error) {
[downloader invalidateSessionAndCancel:YES];
}];
}
- (void)test25ThatDownloadDecryptorWorks {
XCTestExpectation *expectation1 = [self expectationWithDescription:@"Download decryptor for fileURL"];
XCTestExpectation *expectation2 = [self expectationWithDescription:@"Download decryptor for webURL"];
XCTestExpectation *expectation3 = [self expectationWithDescription:@"Download decryptor invalid data"];
SDWebImageDownloader *downloader = [[SDWebImageDownloader alloc] init];
downloader.decryptor = SDWebImageDownloaderDecryptor.base64Decryptor;
// 1. Test fileURL with Base64 encoded data works
NSData *PNGData = [NSData dataWithContentsOfFile:[self testPNGPath]];
@ -565,9 +606,7 @@
}];
// 2. Test webURL with Zip encoded data works
SDWebImageDownloaderResponseModifier *responseModifier = [SDWebImageDownloaderResponseModifier responseModifierWithResponseBlock:^NSURLResponse * _Nullable(NSURLResponse * _Nonnull response) {
return response;
} dataBlock:^NSData * _Nullable(NSData * _Nonnull data, NSURLResponse * _Nullable response) {
SDWebImageDownloaderDecryptor *decryptor = [SDWebImageDownloaderDecryptor decryptorWithBlock:^NSData * _Nullable(NSData * _Nonnull data, NSURLResponse * _Nullable response) {
if (@available(iOS 13, macOS 10.15, *)) {
return [data decompressedDataUsingAlgorithm:NSDataCompressionAlgorithmZlib error:nil];
} else if (@available (iOS 9, macOS 10.11, *)) {
@ -582,37 +621,25 @@
// Note this is not a Zip Archive, just PNG raw buffer data using zlib compression
NSURL *zipURL = [NSURL URLWithString:@"https://github.com/SDWebImage/SDWebImage/files/3728087/SDWebImage_logo_small.png.zip"];
[downloader downloadImageWithURL:zipURL options:0 context:@{SDWebImageContextDownloadResponseModifier : responseModifier} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
[downloader downloadImageWithURL:zipURL options:0 context:@{SDWebImageContextDownloadDecryptor : decryptor} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
expect(error).to.beNil();
expect(image).notTo.beNil();
[expectation2 fulfill];
}];
// 3. Test nil response will cancel the download
responseModifier = [SDWebImageDownloaderResponseModifier responseModifierWithResponseBlock:^NSURLResponse * _Nullable(NSURLResponse * _Nonnull response) {
// 3. Test nil data will mark download failed
decryptor = [SDWebImageDownloaderDecryptor decryptorWithBlock:^NSData * _Nullable(NSData * _Nonnull data, NSURLResponse * _Nullable response) {
return nil;
} dataBlock:^NSData * _Nullable(NSData * _Nonnull data, NSURLResponse * _Nullable response) {
return data;
}];
[downloader downloadImageWithURL:[NSURL URLWithString:kTestJPEGURL] options:0 context:@{SDWebImageContextDownloadResponseModifier : responseModifier} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
[downloader downloadImageWithURL:[NSURL URLWithString:kTestJPEGURL] options:0 context:@{SDWebImageContextDownloadDecryptor : decryptor} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
expect(error).notTo.beNil();
expect(error.code).equal(SDWebImageErrorInvalidDownloadResponse);
expect(error.code).equal(SDWebImageErrorBadImageData);
[expectation3 fulfill];
}];
// 4. Test nil data will mark download failed
responseModifier = [SDWebImageDownloaderResponseModifier responseModifierWithResponseBlock:^NSURLResponse * _Nullable(NSURLResponse * _Nonnull response) {
return response;
} dataBlock:^NSData * _Nullable(NSData * _Nonnull data, NSURLResponse * _Nullable response) {
return nil;
[self waitForExpectationsWithCommonTimeoutUsingHandler:^(NSError * _Nullable error) {
[downloader invalidateSessionAndCancel:YES];
}];
[downloader downloadImageWithURL:[NSURL URLWithString:kTestJPEGURL] options:0 context:@{SDWebImageContextDownloadResponseModifier : responseModifier} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
expect(error).notTo.beNil();
expect(error.code).equal(SDWebImageErrorInvalidDownloadResponse);
[expectation4 fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
#pragma mark - SDWebImageLoader

View File

@ -37,6 +37,7 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[];
#import <SDWebImage/SDWebImageDownloaderOperation.h>
#import <SDWebImage/SDWebImageDownloaderRequestModifier.h>
#import <SDWebImage/SDWebImageDownloaderResponseModifier.h>
#import <SDWebImage/SDWebImageDownloaderDecryptor.h>
#import <SDWebImage/SDImageLoader.h>
#import <SDWebImage/SDImageLoadersManager.h>
#import <SDWebImage/UIButton+WebCache.h>