Merge pull request #2866 from dreampiggy/feature_response_modifier_data_decrypter

Feature response modifier data decrypter
This commit is contained in:
DreamPiggy 2019-10-16 18:59:30 +08:00 committed by GitHub
commit 75fb66c834
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 408 additions and 5 deletions

View File

@ -60,9 +60,17 @@
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 */; };
32542763235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 32542761235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.h */; settings = {ATTRIBUTES = (Public, ); }; };
32542764235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 32542762235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.m */; };
32542765235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 32542762235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.m */; };
325427662355783C0042BAA4 /* SDWebImageDownloaderResponseModifier.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 32542761235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.h */; };
3257EAFA21898AED0097B271 /* SDImageGraphics.h in Headers */ = {isa = PBXBuildFile; fileRef = 3257EAF721898AED0097B271 /* SDImageGraphics.h */; settings = {ATTRIBUTES = (Public, ); }; };
3257EAFC21898AED0097B271 /* SDImageGraphics.m in Sources */ = {isa = PBXBuildFile; fileRef = 3257EAF821898AED0097B271 /* SDImageGraphics.m */; };
3257EAFD21898AED0097B271 /* SDImageGraphics.m in Sources */ = {isa = PBXBuildFile; fileRef = 3257EAF821898AED0097B271 /* SDImageGraphics.m */; };
@ -280,6 +288,8 @@
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 */,
32E5690822B1FFCA00CBABC6 /* SDWebImageOptionsProcessor.h in Copy Headers */,
@ -368,8 +378,12 @@
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>"; };
32542762235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDownloaderResponseModifier.m; path = Core/SDWebImageDownloaderResponseModifier.m; sourceTree = "<group>"; };
3257EAF721898AED0097B271 /* SDImageGraphics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDImageGraphics.h; path = Core/SDImageGraphics.h; sourceTree = "<group>"; };
3257EAF821898AED0097B271 /* SDImageGraphics.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDImageGraphics.m; path = Core/SDImageGraphics.m; sourceTree = "<group>"; };
325C460022339330004CAE11 /* SDImageAssetManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDImageAssetManager.h; sourceTree = "<group>"; };
@ -757,6 +771,10 @@
32B9B536206ED4230026769D /* SDWebImageDownloaderConfig.m */,
32F21B4F20788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.h */,
32F21B5020788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.m */,
32542761235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.h */,
32542762235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.m */,
3250C9EC2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.h */,
3250C9ED2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m */,
321B377D2083290D00C0EA77 /* SDImageLoader.h */,
321B377E2083290D00C0EA77 /* SDImageLoader.m */,
321B377F2083290E00C0EA77 /* SDImageLoadersManager.h */,
@ -833,6 +851,7 @@
4A2CAE181AB4BB6400B6BC39 /* SDWebImageCompat.h in Headers */,
4A2CAE331AB4BB7500B6BC39 /* UIImageView+HighlightedWebCache.h in Headers */,
328BB6C32082581100760D6C /* SDDiskCache.h in Headers */,
32542763235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.h in Headers */,
4A2CAE1D1AB4BB6800B6BC39 /* SDWebImageDownloaderOperation.h in Headers */,
4A2CAE2B1AB4BB7500B6BC39 /* UIButton+WebCache.h in Headers */,
4A2CAE251AB4BB7000B6BC39 /* SDWebImagePrefetcher.h in Headers */,
@ -854,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 */,
@ -1054,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 */,
@ -1073,6 +1094,7 @@
3298655E2337230C0071958B /* SDImageHEICCoder.m in Sources */,
32F7C0802030719600873181 /* UIImage+Transform.m in Sources */,
327054DC206CD8B3006EA328 /* SDImageAPNGCoder.m in Sources */,
32542765235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.m in Sources */,
325312D0200F09910046BF1E /* SDWebImageTransition.m in Sources */,
321E609C1F38E8ED00405457 /* SDImageIOCoder.m in Sources */,
4A2CAE261AB4BB7000B6BC39 /* SDWebImagePrefetcher.m in Sources */,
@ -1119,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 */,
@ -1138,6 +1161,7 @@
3298655D2337230C0071958B /* SDImageHEICCoder.m in Sources */,
321E609A1F38E8ED00405457 /* SDImageIOCoder.m in Sources */,
327054DA206CD8B3006EA328 /* SDImageAPNGCoder.m in Sources */,
32542764235576E20042BAA4 /* SDWebImageDownloaderResponseModifier.m in Sources */,
325312CE200F09910046BF1E /* SDWebImageTransition.m in Sources */,
5376130C155AD0D5005750A4 /* SDWebImageManager.m in Sources */,
5376130D155AD0D5005750A4 /* SDWebImagePrefetcher.m in Sources */,

View File

@ -233,6 +233,16 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextAnimat
*/
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextDownloadRequestModifier;
/**
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

@ -126,5 +126,7 @@ SDWebImageContextOption const SDWebImageContextStoreCacheType = @"storeCacheType
SDWebImageContextOption const SDWebImageContextOriginalStoreCacheType = @"originalStoreCacheType";
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

@ -12,6 +12,8 @@
#import "SDWebImageOperation.h"
#import "SDWebImageDownloaderConfig.h"
#import "SDWebImageDownloaderRequestModifier.h"
#import "SDWebImageDownloaderResponseModifier.h"
#import "SDWebImageDownloaderDecryptor.h"
#import "SDImageLoader.h"
/// Downloader options
@ -142,12 +144,29 @@ typedef SDImageLoaderCompletedBlock SDWebImageDownloaderCompletedBlock;
/**
* Set the request modifier to modify the original download request before image load.
* This request modifier method will be called for each downloading image request. Return the original request means no modication. Return nil will cancel the download request.
* This request modifier method will be called for each downloading image request. Return the original request means no modification. Return nil will cancel the download request.
* Defaults to nil, means does not modify the original download request.
* @note If you want to modify single request, consider using `SDWebImageContextDownloadRequestModifier` context option.
*/
@property (nonatomic, strong, nullable) id<SDWebImageDownloaderRequestModifier> requestModifier;
/**
* 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,16 @@ 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]) {
requestModifier = [context valueForKey:SDWebImageContextDownloadRequestModifier];
@ -291,6 +301,30 @@ static void * SDWebImageDownloaderContext = &SDWebImageDownloaderContext;
} else {
request = [mutableRequest copy];
}
// Response Modifier
id<SDWebImageDownloaderResponseModifier> responseModifier;
if ([context valueForKey:SDWebImageContextDownloadResponseModifier]) {
responseModifier = [context valueForKey:SDWebImageContextDownloadResponseModifier];
} else {
responseModifier = self.responseModifier;
}
if (responseModifier) {
mutableContext[SDWebImageContextDownloadResponseModifier] = responseModifier;
}
// 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)]) {
// Custom operation class

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 be 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

@ -9,6 +9,8 @@
#import "SDWebImageDownloaderOperation.h"
#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))
@ -39,6 +41,9 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
@property (strong, nonatomic, nullable) NSError *responseError;
@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;
@ -76,6 +81,8 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
_options = options;
_context = [context copy];
_callbackBlocks = [NSMutableArray new];
_responseModifier = context[SDWebImageContextDownloadResponseModifier];
_decryptor = context[SDWebImageContextDownloadDecryptor];
_executing = NO;
_finished = NO;
_expectedSize = 0;
@ -293,13 +300,27 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
// Check response modifier, if return nil, will marked as cancelled.
BOOL valid = YES;
if (self.responseModifier && response) {
response = [self.responseModifier modifiedResponseWithResponse:response];
if (!response) {
valid = NO;
self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidDownloadResponse userInfo:nil];
}
}
NSInteger expected = (NSInteger)response.expectedContentLength;
expected = expected > 0 ? expected : 0;
self.expectedSize = expected;
self.response = response;
NSInteger statusCode = [response respondsToSelector:@selector(statusCode)] ? ((NSHTTPURLResponse *)response).statusCode : 200;
BOOL valid = statusCode >= 200 && statusCode < 400;
if (!valid) {
// Status code should between [200,400)
BOOL statusCodeValid = statusCode >= 200 && statusCode < 400;
if (!statusCodeValid) {
valid = NO;
self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidDownloadStatusCode userInfo:@{SDWebImageErrorDownloadStatusCodeKey : @(statusCode)}];
}
//'304 Not Modified' is an exceptional one
@ -353,8 +374,10 @@ didReceiveResponse:(NSURLResponse *)response
return;
}
self.previousProgress = currentProgress;
if (self.options & SDWebImageDownloaderProgressiveLoad) {
// 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];
@ -421,6 +444,10 @@ didReceiveResponse:(NSURLResponse *)response
if ([self callbacksForKey:kCompletedCallbackKey].count > 0) {
NSData *imageData = [self.imageData copy];
self.imageData = nil;
// 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`,
* then we should check if the cached data is equal to image data

View File

@ -17,6 +17,9 @@ typedef NSURLRequest * _Nullable (^SDWebImageDownloaderRequestModifierBlock)(NSU
*/
@protocol SDWebImageDownloaderRequestModifier <NSObject>
/// Modify the original URL request and return a new one instead. You can modify the HTTP header, cachePolicy, etc for this URL.
/// @param request The original URL request for image loading
/// @note If return nil, the URL request will be cancelled.
- (nullable NSURLRequest *)modifiedRequestWithRequest:(nonnull NSURLRequest *)request;
@end

View File

@ -0,0 +1,35 @@
/*
* 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 NSURLResponse * _Nullable (^SDWebImageDownloaderResponseModifierBlock)(NSURLResponse * _Nonnull response);
/**
This is the protocol for downloader response modifier.
We can use a block to specify the downloader response modifier. 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 SDWebImageDownloaderResponseModifier <NSObject>
/// 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`
- (nullable NSURLResponse *)modifiedResponseWithResponse:(nonnull NSURLResponse *)response;
@end
/**
A downloader response modifier class with block.
*/
@interface SDWebImageDownloaderResponseModifier : NSObject <SDWebImageDownloaderResponseModifier>
- (nonnull instancetype)initWithBlock:(nonnull SDWebImageDownloaderResponseModifierBlock)block;
+ (nonnull instancetype)responseModifierWithBlock:(nonnull SDWebImageDownloaderResponseModifierBlock)block;
@end

View File

@ -0,0 +1,40 @@
/*
* 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 "SDWebImageDownloaderResponseModifier.h"
@interface SDWebImageDownloaderResponseModifier ()
@property (nonatomic, copy, nonnull) SDWebImageDownloaderResponseModifierBlock block;
@end
@implementation SDWebImageDownloaderResponseModifier
- (instancetype)initWithBlock:(SDWebImageDownloaderResponseModifierBlock)block {
self = [super init];
if (self) {
self.block = block;
}
return self;
}
+ (instancetype)responseModifierWithBlock:(SDWebImageDownloaderResponseModifierBlock)block {
SDWebImageDownloaderResponseModifier *responseModifier = [[SDWebImageDownloaderResponseModifier alloc] initWithBlock:block];
return responseModifier;
}
- (nullable NSURLResponse *)modifiedResponseWithResponse:(nonnull NSURLResponse *)response {
if (!self.block) {
return nil;
}
return self.block(response);
}
@end

View File

@ -22,4 +22,5 @@ typedef NS_ERROR_ENUM(SDWebImageErrorDomain, SDWebImageError) {
SDWebImageErrorInvalidDownloadOperation = 2000, // The image download operation is invalid, such as nil operation or unexpected error occur when operation initialized
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.
SDWebImageErrorInvalidDownloadResponse = 2003, // When using response modifier, the modified download response is nil and marked as cancelled.
};

View File

@ -11,6 +11,7 @@
#import "SDWebImageTestDownloadOperation.h"
#import "SDWebImageTestCoder.h"
#import "SDWebImageTestLoader.h"
#import <compression.h>
#define kPlaceholderTestURLTemplate @"https://via.placeholder.com/10000x%d.png"
@ -542,6 +543,105 @@
[self waitForExpectationsWithCommonTimeout];
}
- (void)test24ThatDownloadResponseModifierWorks {
XCTestExpectation *expectation1 = [self expectationWithDescription:@"Download response modifier for webURL"];
XCTestExpectation *expectation2 = [self expectationWithDescription:@"Download response modifier invalid response"];
SDWebImageDownloader *downloader = [[SDWebImageDownloader alloc] init];
// 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]];
NSData *base64PNGData = [PNGData base64EncodedDataWithOptions:0];
expect(base64PNGData).notTo.beNil();
NSURL *base64FileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"TestBase64.png"]];
[base64PNGData writeToURL:base64FileURL atomically:YES];
[downloader downloadImageWithURL:base64FileURL options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
expect(error).to.beNil();
expect(image).notTo.beNil();
[expectation1 fulfill];
}];
// 2. Test webURL with Zip encoded data works
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, *)) {
NSMutableData *decodedData = [NSMutableData dataWithLength:10 * data.length];
compression_decode_buffer((uint8_t *)decodedData.bytes, decodedData.length, data.bytes, data.length, nil, COMPRESSION_ZLIB);
return [decodedData copy];
} else {
// iOS 8 does not have built-in Zlib support, just mock the data
return base64PNGData;
}
}];
// 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:@{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 data will mark download failed
decryptor = [SDWebImageDownloaderDecryptor decryptorWithBlock:^NSData * _Nullable(NSData * _Nonnull data, NSURLResponse * _Nullable response) {
return nil;
}];
[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(SDWebImageErrorBadImageData);
[expectation3 fulfill];
}];
[self waitForExpectationsWithCommonTimeoutUsingHandler:^(NSError * _Nullable error) {
[downloader invalidateSessionAndCancel:YES];
}];
}
#pragma mark - SDWebImageLoader
- (void)test30CustomImageLoaderWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Custom image not works"];
@ -585,4 +685,11 @@
[self waitForExpectationsWithCommonTimeout];
}
#pragma mark - Helper
- (NSString *)testPNGPath {
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
return [testBundle pathForResource:@"TestImage" ofType:@"png"];
}
@end

View File

@ -36,6 +36,8 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[];
#import <SDWebImage/SDWebImageDownloaderConfig.h>
#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>