mirror of
https://github.com/status-im/react-native.git
synced 2025-02-23 22:58:19 +00:00
Enhance image freshness check before stored into cache (#23226)
Summary:
This is a re-submit of D13895627 which got landed but didn't include a fix to Instagram's code. The sheriffs were unsure how it got landed without running the build.
Currently, before we store the image to cache, we only respect `Cache-Control`, actually, we also may need to check `Expires`、`Last-Modified`, refer to [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#Freshness), and [okhttp](568a91c44a/okhttp/src/main/java/okhttp3/internal/cache/CacheStrategy.java (L268)
) respect the `MDN`, so in iOS, we can also respect this.
[iOS] [Fixed] - Respect `MDN` cache strategy before cache the image.
Reviewed By: shergin
Differential Revision: D13896822
Pulled By: cpojer
fbshipit-source-id: 8c1714f4a17ad40496146806cff3e188a60be93c
This commit is contained in:
parent
a4840e7ae3
commit
fb8ba3fe95
@ -106,33 +106,49 @@ static NSString *RCTCacheKeyForImage(NSString *imageTag, CGSize size, CGFloat sc
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(RCTResizeMode)resizeMode
|
||||
responseDate:(NSString *)responseDate
|
||||
cacheControl:(NSString *)cacheControl
|
||||
response:(NSURLResponse *)response
|
||||
{
|
||||
NSString *cacheKey = RCTCacheKeyForImage(url, size, scale, resizeMode);
|
||||
BOOL shouldCache = YES;
|
||||
NSDate *staleTime;
|
||||
NSArray<NSString *> *components = [cacheControl componentsSeparatedByString:@","];
|
||||
for (NSString *component in components) {
|
||||
if ([component containsString:@"no-cache"] || [component containsString:@"no-store"] || [component hasSuffix:@"max-age=0"]) {
|
||||
shouldCache = NO;
|
||||
break;
|
||||
} else {
|
||||
NSRange range = [component rangeOfString:@"max-age="];
|
||||
if (range.location != NSNotFound) {
|
||||
NSInteger seconds = [[component substringFromIndex:range.location + range.length] integerValue];
|
||||
NSDate *originalDate = [self dateWithHeaderString:responseDate];
|
||||
staleTime = [originalDate dateByAddingTimeInterval:(NSTimeInterval)seconds];
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSString *cacheKey = RCTCacheKeyForImage(url, size, scale, resizeMode);
|
||||
BOOL shouldCache = YES;
|
||||
NSString *responseDate = ((NSHTTPURLResponse *)response).allHeaderFields[@"Date"];
|
||||
NSDate *originalDate = [self dateWithHeaderString:responseDate];
|
||||
NSString *cacheControl = ((NSHTTPURLResponse *)response).allHeaderFields[@"Cache-Control"];
|
||||
NSDate *staleTime;
|
||||
NSArray<NSString *> *components = [cacheControl componentsSeparatedByString:@","];
|
||||
for (NSString *component in components) {
|
||||
if ([component containsString:@"no-cache"] || [component containsString:@"no-store"] || [component hasSuffix:@"max-age=0"]) {
|
||||
shouldCache = NO;
|
||||
break;
|
||||
} else {
|
||||
NSRange range = [component rangeOfString:@"max-age="];
|
||||
if (range.location != NSNotFound) {
|
||||
NSInteger seconds = [[component substringFromIndex:range.location + range.length] integerValue];
|
||||
staleTime = [originalDate dateByAddingTimeInterval:(NSTimeInterval)seconds];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldCache) {
|
||||
if (staleTime) {
|
||||
@synchronized(_cacheStaleTimes) {
|
||||
_cacheStaleTimes[cacheKey] = staleTime;
|
||||
if (shouldCache) {
|
||||
if (!staleTime && originalDate) {
|
||||
NSString *expires = ((NSHTTPURLResponse *)response).allHeaderFields[@"Expires"];
|
||||
NSString *lastModified = ((NSHTTPURLResponse *)response).allHeaderFields[@"Last-Modified"];
|
||||
if (expires) {
|
||||
staleTime = [self dateWithHeaderString:expires];
|
||||
} else if (lastModified) {
|
||||
NSDate *lastModifiedDate = [self dateWithHeaderString:lastModified];
|
||||
if (lastModifiedDate) {
|
||||
NSTimeInterval interval = [originalDate timeIntervalSinceDate:lastModifiedDate] / 10;
|
||||
staleTime = [originalDate dateByAddingTimeInterval:interval];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (staleTime) {
|
||||
@synchronized(_cacheStaleTimes) {
|
||||
_cacheStaleTimes[cacheKey] = staleTime;
|
||||
}
|
||||
}
|
||||
return [self addImageToCache:image forKey:cacheKey];
|
||||
}
|
||||
return [self addImageToCache:image forKey:cacheKey];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,8 +31,7 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock;
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(RCTResizeMode)resizeMode
|
||||
responseDate:(NSString *)responseDate
|
||||
cacheControl:(NSString *)cacheControl;
|
||||
response:(NSURLResponse *)response;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -346,7 +346,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
resizeMode:(RCTResizeMode)resizeMode
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
|
||||
partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
|
||||
completionBlock:(void (^)(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate, NSString *cacheControl))completionBlock
|
||||
completionBlock:(void (^)(NSError *error, id imageOrData, BOOL cacheResult, NSURLResponse *response))completionBlock
|
||||
{
|
||||
{
|
||||
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
||||
@ -375,7 +375,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
__block atomic_bool cancelled = ATOMIC_VAR_INIT(NO);
|
||||
// TODO: Protect this variable shared between threads.
|
||||
__block dispatch_block_t cancelLoad = nil;
|
||||
void (^completionHandler)(NSError *, id, NSString *, NSString *) = ^(NSError *error, id imageOrData, NSString *fetchDate, NSString *cacheControl) {
|
||||
void (^completionHandler)(NSError *, id, NSURLResponse *) = ^(NSError *error, id imageOrData, NSURLResponse *response) {
|
||||
cancelLoad = nil;
|
||||
|
||||
// If we've received an image, we should try to set it synchronously,
|
||||
@ -385,11 +385,11 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
// expecting it, and may do expensive post-processing in the callback
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
if (!atomic_load(&cancelled)) {
|
||||
completionBlock(error, imageOrData, cacheResult, fetchDate, cacheControl);
|
||||
completionBlock(error, imageOrData, cacheResult, response);
|
||||
}
|
||||
});
|
||||
} else if (!atomic_load(&cancelled)) {
|
||||
completionBlock(error, imageOrData, cacheResult, fetchDate, cacheControl);
|
||||
completionBlock(error, imageOrData, cacheResult, response);
|
||||
}
|
||||
};
|
||||
|
||||
@ -403,7 +403,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
progressHandler:progressHandler
|
||||
partialLoadHandler:partialLoadHandler
|
||||
completionHandler:^(NSError *error, UIImage *image){
|
||||
completionHandler(error, image, nil, nil);
|
||||
completionHandler(error, image, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
@ -427,7 +427,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
progressHandler:progressHandler
|
||||
partialLoadHandler:partialLoadHandler
|
||||
completionHandler:^(NSError *error, UIImage *image) {
|
||||
completionHandler(error, image, nil, nil);
|
||||
completionHandler(error, image, nil);
|
||||
}];
|
||||
} else {
|
||||
UIImage *image;
|
||||
@ -439,7 +439,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
}
|
||||
|
||||
if (image) {
|
||||
completionHandler(nil, image, nil, nil);
|
||||
completionHandler(nil, image, nil);
|
||||
} else {
|
||||
// Use networking module to load image
|
||||
cancelLoad = [strongSelf _loadURLRequest:request
|
||||
@ -464,7 +464,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)_loadURLRequest:(NSURLRequest *)request
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
|
||||
completionBlock:(void (^)(NSError *error, id imageOrData, NSString *fetchDate, NSString *cacheControl))completionHandler
|
||||
completionBlock:(void (^)(NSError *error, id imageOrData, NSURLResponse *response))completionHandler
|
||||
{
|
||||
// Check if networking module is available
|
||||
if (RCT_DEBUG && ![_bridge respondsToSelector:@selector(networking)]) {
|
||||
@ -486,19 +486,17 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
RCTURLRequestCompletionBlock processResponse = ^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
// Check for system errors
|
||||
if (error) {
|
||||
completionHandler(error, nil, nil, nil);
|
||||
completionHandler(error, nil, response);
|
||||
return;
|
||||
} else if (!response) {
|
||||
completionHandler(RCTErrorWithMessage(@"Response metadata error"), nil, nil, nil);
|
||||
completionHandler(RCTErrorWithMessage(@"Response metadata error"), nil, response);
|
||||
return;
|
||||
} else if (!data) {
|
||||
completionHandler(RCTErrorWithMessage(@"Unknown image download error"), nil, nil, nil);
|
||||
completionHandler(RCTErrorWithMessage(@"Unknown image download error"), nil, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for http errors
|
||||
NSString *responseDate;
|
||||
NSString *cacheControl;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
|
||||
if (statusCode != 200) {
|
||||
@ -506,16 +504,13 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: errorMessage};
|
||||
completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain
|
||||
code:statusCode
|
||||
userInfo:userInfo], nil, nil, nil);
|
||||
userInfo:userInfo], nil, response);
|
||||
return;
|
||||
}
|
||||
|
||||
responseDate = ((NSHTTPURLResponse *)response).allHeaderFields[@"Date"];
|
||||
cacheControl = ((NSHTTPURLResponse *)response).allHeaderFields[@"Cache-Control"];
|
||||
}
|
||||
|
||||
// Call handler
|
||||
completionHandler(nil, data, responseDate, cacheControl);
|
||||
completionHandler(nil, data, response);
|
||||
};
|
||||
|
||||
// Download image
|
||||
@ -537,7 +532,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
} else {
|
||||
someError = RCTErrorWithMessage(@"Unknown image download error");
|
||||
}
|
||||
completionHandler(someError, nil, nil, nil);
|
||||
completionHandler(someError, nil, response);
|
||||
[strongSelf dequeueTasks];
|
||||
return;
|
||||
}
|
||||
@ -603,7 +598,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
};
|
||||
|
||||
__weak RCTImageLoader *weakSelf = self;
|
||||
void (^completionHandler)(NSError *, id, BOOL, NSString *, NSString *) = ^(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate, NSString *cacheControl) {
|
||||
void (^completionHandler)(NSError *, id, BOOL, NSURLResponse *) = ^(NSError *error, id imageOrData, BOOL cacheResult, NSURLResponse *response) {
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
if (atomic_load(&cancelled) || !strongSelf) {
|
||||
return;
|
||||
@ -623,8 +618,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
size:size
|
||||
scale:scale
|
||||
resizeMode:resizeMode
|
||||
responseDate:fetchDate
|
||||
cacheControl:cacheControl];
|
||||
response:response];
|
||||
}
|
||||
|
||||
cancelLoad = nil;
|
||||
@ -758,7 +752,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
- (RCTImageLoaderCancellationBlock)getImageSizeForURLRequest:(NSURLRequest *)imageURLRequest
|
||||
block:(void(^)(NSError *error, CGSize size))callback
|
||||
{
|
||||
void (^completion)(NSError *, id, BOOL, NSString *, NSString *) = ^(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate, NSString *cacheControl) {
|
||||
void (^completion)(NSError *, id, BOOL, NSURLResponse *) = ^(NSError *error, id imageOrData, BOOL cacheResult, NSURLResponse *response) {
|
||||
CGSize size;
|
||||
if ([imageOrData isKindOfClass:[NSData class]]) {
|
||||
NSDictionary *meta = RCTGetImageMetadata(imageOrData);
|
||||
|
Loading…
x
Reference in New Issue
Block a user