Skip to content

Commit 7ca2c45

Browse files
committed
Merge pull request steipete#21 from arturgrigor/master
Allow SDURLCache to be subclassed instead of hijacking SDURLCache's methods
2 parents 1c08ac0 + 9a62f4c commit 7ca2c45

File tree

3 files changed

+74
-35
lines changed

3 files changed

+74
-35
lines changed

SDURLCache.h

+30-8
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@
1010

1111
#import <Foundation/Foundation.h>
1212

13-
@interface SDURLCache : NSURLCache {
13+
/* Define SDURLCACHE_DEBUG=1 if you want to get some more info about the cached requests. */
14+
15+
@interface SDURLCache : NSURLCache
16+
{
1417
@private
15-
BOOL _diskCacheInfoDirty;
16-
BOOL _ignoreMemoryOnlyStoragePolicy;
17-
BOOL _timerPaused;
18-
NSUInteger _diskCacheUsage;
19-
NSTimeInterval _minCacheInterval;
20-
dispatch_source_t _maintenanceTimer;
18+
BOOL _diskCacheInfoDirty;
19+
BOOL _ignoreMemoryOnlyStoragePolicy;
20+
BOOL _timerPaused;
21+
BOOL _shouldRespectCacheControlHeaders;
22+
NSUInteger _diskCacheUsage;
23+
NSTimeInterval _minCacheInterval;
24+
dispatch_source_t _maintenanceTimer;
2125
}
2226

2327
/*
@@ -38,7 +42,6 @@
3842
*/
3943
@property (nonatomic, assign) BOOL ignoreMemoryOnlyStoragePolicy;
4044

41-
4245
/*
4346
* Allow caching responses for a request with a cache policy that ignores the local cache.
4447
* Usually, these responses don't need to be cached, because a request that ignores the cache
@@ -48,6 +51,13 @@
4851
*/
4952
@property (nonatomic, assign) BOOL allowCachingResponsesToNonCachedRequests;
5053

54+
/*
55+
* If the server sends cache-control / pragma: no-cache headers, then the requests will not be cached.
56+
*
57+
* The default value is YES.
58+
*/
59+
@property (nonatomic, assign) BOOL shouldRespectCacheControlHeaders;
60+
5161
/*
5262
* Returns a default cache director path to be used at cache initialization. The generated path directory
5363
* will be located in the application's cache directory and thus won't be synced by iTunes.
@@ -66,3 +76,15 @@
6676
- (void)removeAllCachedResponsesInMemory;
6777

6878
@end
79+
80+
#pragma mark - For subclasses only
81+
82+
@interface SDURLCache (PrivateMethods)
83+
84+
/*
85+
* This is the perfect place to strip some query parameters in case you are sending
86+
* OAuth information via query string instead of the Authorization request header.
87+
*/
88+
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
89+
90+
@end

SDURLCache.m

+39-20
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
static NSString *const kAFURLCacheInfoFileName = @"cacheInfo.plist";
3636
static NSString *const kAFURLCacheInfoAccessesKey = @"accesses";
3737
static NSString *const kAFURLCacheInfoSizesKey = @"sizes";
38+
static NSString *const kAFURLCacheInfoURLsKey = @"URLs";
3839
static float const kAFURLCacheLastModFraction = 0.1f; // 10% since Last-Modified suggested by RFC2616 section 13.2.4
3940
static float const kAFURLCacheDefault = 3600.0f; // Default cache expiration delay if none defined (1 hour)
4041

@@ -419,7 +420,7 @@ + (NSDate *)expirationDateFromHeaders:(NSDictionary *)headers withStatusCode:(NS
419420
// Define "now" based on the request
420421
NSString *date = [headers objectForKey:@"Date"];
421422
// If no Date: header, define now from local clock
422-
NSDate *now = date ? [SDURLCache dateFromHttpDateString:date] : [NSDate date];
423+
NSDate *now = date ? [[self class] dateFromHttpDateString:date] : [NSDate date];
423424

424425
// Look at info from the Cache-Control: max-age=n header
425426
NSString *cacheControl = [[headers objectForKey:@"Cache-Control"] lowercaseString];
@@ -447,7 +448,7 @@ + (NSDate *)expirationDateFromHeaders:(NSDictionary *)headers withStatusCode:(NS
447448
NSString *expires = [headers objectForKey:@"Expires"];
448449
if (expires) {
449450
NSTimeInterval expirationInterval = 0;
450-
NSDate *expirationDate = [SDURLCache dateFromHttpDateString:expires];
451+
NSDate *expirationDate = [[self class] dateFromHttpDateString:expires];
451452
if (expirationDate) {
452453
expirationInterval = [expirationDate timeIntervalSinceDate:now];
453454
}
@@ -470,7 +471,7 @@ + (NSDate *)expirationDateFromHeaders:(NSDictionary *)headers withStatusCode:(NS
470471
NSString *lastModified = [headers objectForKey:@"Last-Modified"];
471472
if (lastModified) {
472473
NSTimeInterval age = 0;
473-
NSDate *lastModifiedDate = [SDURLCache dateFromHttpDateString:lastModified];
474+
NSDate *lastModifiedDate = [[self class] dateFromHttpDateString:lastModified];
474475
if (lastModifiedDate) {
475476
// Define the age of the document by comparing the Date header with the Last-Modified header
476477
age = [now timeIntervalSinceDate:lastModifiedDate];
@@ -491,6 +492,9 @@ - (NSMutableDictionary *)diskCacheInfo {
491492
_diskCacheInfo = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
492493
[NSMutableDictionary dictionary], kAFURLCacheInfoAccessesKey,
493494
[NSMutableDictionary dictionary], kAFURLCacheInfoSizesKey,
495+
#if SDURLCACHE_DEBUG
496+
[NSMutableDictionary dictionary], kAFURLCacheInfoURLsKey,
497+
#endif
494498
nil];
495499
}
496500
_diskCacheInfoDirty = NO;
@@ -541,12 +545,18 @@ - (void)removeCachedResponseForCachedKeys:(NSArray *)cacheKeys {
541545

542546
NSMutableDictionary *accesses = [self.diskCacheInfo objectForKey:kAFURLCacheInfoAccessesKey];
543547
NSMutableDictionary *sizes = [self.diskCacheInfo objectForKey:kAFURLCacheInfoSizesKey];
548+
#if SDURLCACHE_DEBUG
549+
NSMutableDictionary *urls = [self.diskCacheInfo objectForKey:kAFURLCacheInfoURLsKey];
550+
#endif
544551
NSFileManager *fileManager = [[NSFileManager alloc] init];
545552

546553
while ((cacheKey = [enumerator nextObject])) {
547554
NSUInteger cacheItemSize = [[sizes objectForKey:cacheKey] unsignedIntegerValue];
548555
[accesses removeObjectForKey:cacheKey];
549556
[sizes removeObjectForKey:cacheKey];
557+
#if SDURLCACHE_DEBUG
558+
[urls removeObjectForKey:cacheKey];
559+
#endif
550560
[fileManager removeItemAtPath:[_diskCachePath stringByAppendingPathComponent:cacheKey] error:NULL];
551561

552562
_diskCacheUsage -= cacheItemSize;
@@ -583,7 +593,7 @@ - (void)balanceDiskUsage {
583593

584594

585595
- (void)storeRequestToDisk:(NSURLRequest *)request response:(NSCachedURLResponse *)cachedResponse {
586-
NSString *cacheKey = [SDURLCache cacheKeyForURL:request.URL];
596+
NSString *cacheKey = [[self class] cacheKeyForURL:request.URL];
587597
NSString *cacheFilePath = [_diskCachePath stringByAppendingPathComponent:cacheKey];
588598

589599
[self createDiskCachePath];
@@ -606,6 +616,9 @@ - (void)storeRequestToDisk:(NSURLRequest *)request response:(NSCachedURLResponse
606616
// Update cache info for the stored item
607617
[(NSMutableDictionary *)[self.diskCacheInfo objectForKey:kAFURLCacheInfoAccessesKey] setObject:[NSDate date] forKey:cacheKey];
608618
[(NSMutableDictionary *)[self.diskCacheInfo objectForKey:kAFURLCacheInfoSizesKey] setObject:cacheItemSize forKey:cacheKey];
619+
#if SDURLCACHE_DEBUG
620+
[(NSMutableDictionary *)[self.diskCacheInfo objectForKey:kAFURLCacheInfoURLsKey] setObject:request.URL.absoluteString forKey:cacheKey];
621+
#endif
609622

610623
[self saveCacheInfo];
611624

@@ -643,6 +656,7 @@ + (NSString *)defaultCachePath {
643656
- (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path {
644657
if ((self = [super initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:path])) {
645658
self.minCacheInterval = kAFURLCacheInfoDefaultMinCacheInterval;
659+
self.shouldRespectCacheControlHeaders = YES;
646660
self.diskCachePath = path;
647661
self.ignoreMemoryOnlyStoragePolicy = NO;
648662
}
@@ -651,7 +665,7 @@ - (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger
651665
}
652666

653667
- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request {
654-
request = [SDURLCache canonicalRequestForRequest:request];
668+
request = [[self class] canonicalRequestForRequest:request];
655669

656670
if (!_allowCachingResponsesToNonCachedRequests &&
657671
(request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData
@@ -668,15 +682,19 @@ - (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NS
668682
NSURLCacheStoragePolicy storagePolicy = cachedResponse.storagePolicy;
669683
if ((storagePolicy == NSURLCacheStorageAllowed || (storagePolicy == NSURLCacheStorageAllowedInMemoryOnly && _ignoreMemoryOnlyStoragePolicy))
670684
&& [cachedResponse.response isKindOfClass:[NSHTTPURLResponse self]]
671-
&& cachedResponse.data.length < self.diskCapacity) {
672-
NSDictionary *headers = [(NSHTTPURLResponse *)cachedResponse.response allHeaderFields];
673-
// RFC 2616 section 13.3.4 says clients MUST use Etag in any cache-conditional request if provided by server
674-
if (![headers objectForKey:@"Etag"]) {
675-
NSDate *expirationDate = [SDURLCache expirationDateFromHeaders:headers
676-
withStatusCode:((NSHTTPURLResponse *)cachedResponse.response).statusCode];
677-
if (!expirationDate || [expirationDate timeIntervalSinceNow] - _minCacheInterval <= 0) {
678-
// This response is not cacheable, headers said
679-
return;
685+
&& cachedResponse.data.length < self.diskCapacity)
686+
{
687+
if (self.shouldRespectCacheControlHeaders)
688+
{
689+
NSDictionary *headers = [(NSHTTPURLResponse *)cachedResponse.response allHeaderFields];
690+
// RFC 2616 section 13.3.4 says clients MUST use Etag in any cache-conditional request if provided by server
691+
if (![headers objectForKey:@"Etag"]) {
692+
NSDate *expirationDate = [[self class] expirationDateFromHeaders:headers
693+
withStatusCode:((NSHTTPURLResponse *)cachedResponse.response).statusCode];
694+
if (!expirationDate || [expirationDate timeIntervalSinceNow] - _minCacheInterval <= 0) {
695+
// This response is not cacheable, headers said
696+
return;
697+
}
680698
}
681699
}
682700

@@ -687,14 +705,14 @@ - (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NS
687705
}
688706

689707
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
690-
request = [SDURLCache canonicalRequestForRequest:request];
708+
request = [[self class] canonicalRequestForRequest:request];
691709

692710
NSCachedURLResponse *memoryResponse = [super cachedResponseForRequest:request];
693711
if (memoryResponse) {
694712
return memoryResponse;
695713
}
696714

697-
NSString *cacheKey = [SDURLCache cacheKeyForURL:request.URL];
715+
NSString *cacheKey = [[self class] cacheKeyForURL:request.URL];
698716

699717
// NOTE: We don't handle expiration here as even staled cache data is necessary for NSURLConnection to handle cache revalidation.
700718
// Staled cache data is also needed for cachePolicies which force the use of the cache.
@@ -739,10 +757,10 @@ - (NSUInteger)currentDiskUsage {
739757
}
740758

741759
- (void)removeCachedResponseForRequest:(NSURLRequest *)request {
742-
request = [SDURLCache canonicalRequestForRequest:request];
760+
request = [[self class] canonicalRequestForRequest:request];
743761

744762
[super removeCachedResponseForRequest:request];
745-
[self removeCachedResponseForCachedKeys:[NSArray arrayWithObject:[SDURLCache cacheKeyForURL:request.URL]]];
763+
[self removeCachedResponseForCachedKeys:[NSArray arrayWithObject:[[self class] cacheKeyForURL:request.URL]]];
746764
[self saveCacheInfo];
747765
}
748766

@@ -761,12 +779,12 @@ - (void)removeAllCachedResponsesInMemory {
761779

762780
- (BOOL)isCached:(NSURL *)url {
763781
NSURLRequest *request = [NSURLRequest requestWithURL:url];
764-
request = [SDURLCache canonicalRequestForRequest:request];
782+
request = [[self class] canonicalRequestForRequest:request];
765783

766784
if ([super cachedResponseForRequest:request]) {
767785
return YES;
768786
}
769-
NSString *cacheKey = [SDURLCache cacheKeyForURL:url];
787+
NSString *cacheKey = [[self class] cacheKeyForURL:url];
770788
NSString *cacheFile = [_diskCachePath stringByAppendingPathComponent:cacheKey];
771789

772790
BOOL isCached = [[[NSFileManager alloc] init] fileExistsAtPath:cacheFile];
@@ -787,6 +805,7 @@ - (void)dealloc {
787805
@synthesize minCacheInterval = _minCacheInterval;
788806
@synthesize ignoreMemoryOnlyStoragePolicy = _ignoreMemoryOnlyStoragePolicy;
789807
@synthesize allowCachingResponsesToNonCachedRequests = _allowCachingResponsesToNonCachedRequests;
808+
@synthesize shouldRespectCacheControlHeaders = _shouldRespectCacheControlHeaders;
790809
@synthesize diskCachePath = _diskCachePath;
791810
@synthesize diskCacheInfo = _diskCacheInfo;
792811

SDURLCache.xcodeproj/project.pbxproj

+5-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
archiveVersion = 1;
44
classes = {
55
};
6-
objectVersion = 45;
6+
objectVersion = 46;
77
objects = {
88

99
/* Begin PBXBuildFile section */
@@ -163,8 +163,11 @@
163163
/* Begin PBXProject section */
164164
0867D690FE84028FC02AAC07 /* Project object */ = {
165165
isa = PBXProject;
166+
attributes = {
167+
LastUpgradeCheck = 0460;
168+
};
166169
buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "SDURLCache" */;
167-
compatibilityVersion = "Xcode 3.1";
170+
compatibilityVersion = "Xcode 3.2";
168171
developmentRegion = English;
169172
hasScannedForEncodings = 1;
170173
knownRegions = (
@@ -246,7 +249,6 @@
246249
COPY_PHASE_STRIP = NO;
247250
DSTROOT = /tmp/SDURLCache.dst;
248251
GCC_DYNAMIC_NO_PIC = NO;
249-
GCC_ENABLE_FIX_AND_CONTINUE = YES;
250252
GCC_MODEL_TUNING = G5;
251253
GCC_OPTIMIZATION_LEVEL = 0;
252254
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -348,7 +350,6 @@
348350
"\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
349351
);
350352
GCC_DYNAMIC_NO_PIC = NO;
351-
GCC_ENABLE_FIX_AND_CONTINUE = NO;
352353
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
353354
GCC_OPTIMIZATION_LEVEL = 0;
354355
OTHER_LDFLAGS = (
@@ -359,7 +360,6 @@
359360
"-framework",
360361
UIKit,
361362
);
362-
PREBINDING = NO;
363363
PRODUCT_NAME = SDURLCacheTestBundle;
364364
SDKROOT = iphoneos;
365365
WRAPPER_EXTENSION = octest;
@@ -377,7 +377,6 @@
377377
"\"$(SDKROOT)/Developer/Library/Frameworks\"",
378378
"\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"",
379379
);
380-
GCC_ENABLE_FIX_AND_CONTINUE = NO;
381380
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
382381
INFOPLIST_FILE = "SDURLCacheTestBundle-Info.plist";
383382
OTHER_LDFLAGS = (
@@ -388,7 +387,6 @@
388387
"-framework",
389388
UIKit,
390389
);
391-
PREBINDING = NO;
392390
PRODUCT_NAME = SDURLCacheTestBundle;
393391
SDKROOT = iphoneos;
394392
WRAPPER_EXTENSION = octest;

0 commit comments

Comments
 (0)