Permalink
Browse files

Allow ASIDownloadCache to be used as an NSURLCache

Added replaceURLsWithDataURLs option to ASIWebPageRequest to allow leaving HTML and CSS unchanged (useful with the above change)
  • Loading branch information...
pokeb committed Oct 2, 2010
1 parent 0950643 commit b152a3d0e9997974e4b16cff6ad44c35dd8b9731
View
@@ -69,14 +69,23 @@ typedef enum _ASICacheStoragePolicy {
// When a non-zero maxAge is passed, it should be used as the expiry time for the cached response
- (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge;
-// Should return an NSDictionary of cached headers for the passed request, if it is stored in the cache
-- (NSDictionary *)cachedHeadersForRequest:(ASIHTTPRequest *)request;
+// Should return an NSDictionary of cached headers for the passed URL, if it is stored in the cache
+- (NSDictionary *)cachedResponseHeadersForURL:(NSURL *)url;
-// Should return the cached body of a response for the passed request, if it is stored in the cache
-- (NSData *)cachedResponseDataForRequest:(ASIHTTPRequest *)request;
+// Should return the cached body of a response for the passed URL, if it is stored in the cache
+- (NSData *)cachedResponseDataForURL:(NSURL *)url;
-// Same as the above, but returns a path to the cached response body instead
-- (NSString *)pathToCachedResponseDataForRequest:(ASIHTTPRequest *)request;
+// Returns a path to the cached response data, if it exists
+- (NSString *)pathToCachedResponseDataForURL:(NSURL *)url;
+
+// Returns a path to the cached response headers, if they url
+- (NSString *)pathToCachedResponseHeadersForURL:(NSURL *)request;
+
+// Returns the location to use to store cached response headers for a particular request
+- (NSString *)pathToStoreCachedResponseHeadersForRequest:(ASIHTTPRequest *)request;
+
+// Returns the location to use to store a cached response body for a particular request
+- (NSString *)pathToStoreCachedResponseDataForRequest:(ASIHTTPRequest *)request;
// Clear cached data stored for the passed storage policy
- (void)clearCachedResponsesForStoragePolicy:(ASICacheStoragePolicy)cachePolicy;
@@ -9,7 +9,7 @@
#import <Foundation/Foundation.h>
#import "ASICacheDelegate.h"
-@interface ASIDownloadCache : NSObject <ASICacheDelegate> {
+@interface ASIDownloadCache : NSURLCache <ASICacheDelegate> {
// The default cache policy for this cache
// Requests that store data in the cache will use this cache policy if their cache policy is set to ASIDefaultCachePolicy
View
@@ -16,7 +16,7 @@
static NSString *permanentCacheFolder = @"PermanentStore";
@interface ASIDownloadCache ()
-+ (NSString *)keyForRequest:(ASIHTTPRequest *)request;
++ (NSString *)keyForURL:(NSURL *)url;
@end
@implementation ASIDownloadCache
@@ -95,15 +95,8 @@ - (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval
return;
}
- NSString *path = nil;
- if ([request cacheStoragePolicy] == ASICacheForSessionDurationCacheStoragePolicy) {
- path = [[self storagePath] stringByAppendingPathComponent:sessionCacheFolder];
- } else {
- path = [[self storagePath] stringByAppendingPathComponent:permanentCacheFolder];
- }
- path = [path stringByAppendingPathComponent:[[self class] keyForRequest:request]];
- NSString *metadataPath = [path stringByAppendingPathExtension:@"cachedheaders"];
- NSString *dataPath = [path stringByAppendingPathExtension:@"cacheddata"];
+ NSString *headerPath = [self pathToStoreCachedResponseHeadersForRequest:request];
+ NSString *dataPath = [self pathToStoreCachedResponseDataForRequest:request];
NSMutableDictionary *responseHeaders = [NSMutableDictionary dictionaryWithDictionary:[request responseHeaders]];
if ([request isResponseCompressed]) {
@@ -115,18 +108,36 @@ - (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval
}
// We use this special key to help expire the request when we get a max-age header
[responseHeaders setObject:[[[self class] rfc1123DateFormatter] stringFromDate:[NSDate date]] forKey:@"X-ASIHTTPRequest-Fetch-date"];
- [responseHeaders writeToFile:metadataPath atomically:NO];
+ [responseHeaders writeToFile:headerPath atomically:NO];
if ([request responseData]) {
[[request responseData] writeToFile:dataPath atomically:NO];
- } else if ([request downloadDestinationPath]) {
+ } else if ([request downloadDestinationPath] && ![[request downloadDestinationPath] isEqualToString:dataPath]) {
NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtPath:[request downloadDestinationPath] toPath:dataPath error:&error];
}
[[self accessLock] unlock];
}
-- (NSDictionary *)cachedHeadersForRequest:(ASIHTTPRequest *)request
+- (NSDictionary *)cachedResponseHeadersForURL:(NSURL *)url
+{
+ NSString *path = [self pathToCachedResponseHeadersForURL:url];
+ if (path) {
+ return [NSDictionary dictionaryWithContentsOfFile:path];
+ }
+ return nil;
+}
+
+- (NSData *)cachedResponseDataForURL:(NSURL *)url
+{
+ NSString *path = [self pathToCachedResponseDataForURL:url];
+ if (path) {
+ return [NSData dataWithContentsOfFile:path];
+ }
+ return nil;
+}
+
+- (NSString *)pathToCachedResponseDataForURL:(NSURL *)url
{
[[self accessLock] lock];
if (![self storagePath]) {
@@ -135,32 +146,23 @@ - (NSDictionary *)cachedHeadersForRequest:(ASIHTTPRequest *)request
}
// Look in the session store
NSString *path = [[self storagePath] stringByAppendingPathComponent:sessionCacheFolder];
- NSString *dataPath = [path stringByAppendingPathComponent:[[[self class] keyForRequest:request] stringByAppendingPathExtension:@"cachedheaders"]];
+ NSString *dataPath = [path stringByAppendingPathComponent:[[[self class] keyForURL:url] stringByAppendingPathExtension:@"cacheddata"]];
if ([[NSFileManager defaultManager] fileExistsAtPath:dataPath]) {
[[self accessLock] unlock];
- return [NSDictionary dictionaryWithContentsOfFile:dataPath];
+ return dataPath;
}
// Look in the permanent store
path = [[self storagePath] stringByAppendingPathComponent:permanentCacheFolder];
- dataPath = [path stringByAppendingPathComponent:[[[self class] keyForRequest:request] stringByAppendingPathExtension:@"cachedheaders"]];
+ dataPath = [path stringByAppendingPathComponent:[[[self class] keyForURL:url] stringByAppendingPathExtension:@"cacheddata"]];
if ([[NSFileManager defaultManager] fileExistsAtPath:dataPath]) {
[[self accessLock] unlock];
- return [NSDictionary dictionaryWithContentsOfFile:dataPath];
+ return dataPath;
}
[[self accessLock] unlock];
return nil;
}
-
-- (NSData *)cachedResponseDataForRequest:(ASIHTTPRequest *)request
-{
- NSString *path = [self pathToCachedResponseDataForRequest:request];
- if (path) {
- return [NSData dataWithContentsOfFile:path];
- }
- return nil;
-}
-- (NSString *)pathToCachedResponseDataForRequest:(ASIHTTPRequest *)request
+- (NSString *)pathToCachedResponseHeadersForURL:(NSURL *)url
{
[[self accessLock] lock];
if (![self storagePath]) {
@@ -169,14 +171,14 @@ - (NSString *)pathToCachedResponseDataForRequest:(ASIHTTPRequest *)request
}
// Look in the session store
NSString *path = [[self storagePath] stringByAppendingPathComponent:sessionCacheFolder];
- NSString *dataPath = [path stringByAppendingPathComponent:[[[self class] keyForRequest:request] stringByAppendingPathExtension:@"cacheddata"]];
+ NSString *dataPath = [path stringByAppendingPathComponent:[[[self class] keyForURL:url] stringByAppendingPathExtension:@"cachedheaders"]];
if ([[NSFileManager defaultManager] fileExistsAtPath:dataPath]) {
[[self accessLock] unlock];
return dataPath;
}
// Look in the permanent store
path = [[self storagePath] stringByAppendingPathComponent:permanentCacheFolder];
- dataPath = [path stringByAppendingPathComponent:[[[self class] keyForRequest:request] stringByAppendingPathExtension:@"cacheddata"]];
+ dataPath = [path stringByAppendingPathComponent:[[[self class] keyForURL:url] stringByAppendingPathExtension:@"cachedheaders"]];
if ([[NSFileManager defaultManager] fileExistsAtPath:dataPath]) {
[[self accessLock] unlock];
return dataPath;
@@ -185,19 +187,46 @@ - (NSString *)pathToCachedResponseDataForRequest:(ASIHTTPRequest *)request
return nil;
}
+- (NSString *)pathToStoreCachedResponseDataForRequest:(ASIHTTPRequest *)request
+{
+ [[self accessLock] lock];
+ if (![self storagePath]) {
+ [[self accessLock] unlock];
+ return nil;
+ }
+ NSString *path = [[self storagePath] stringByAppendingPathComponent:([request cacheStoragePolicy] == ASICacheForSessionDurationCacheStoragePolicy ? sessionCacheFolder : permanentCacheFolder)];
+ path = [path stringByAppendingPathComponent:[[[self class] keyForURL:[request url]] stringByAppendingPathExtension:@"cacheddata"]];
+ [[self accessLock] unlock];
+ return path;
+}
+
+- (NSString *)pathToStoreCachedResponseHeadersForRequest:(ASIHTTPRequest *)request
+{
+ [[self accessLock] lock];
+ if (![self storagePath]) {
+ [[self accessLock] unlock];
+ return nil;
+ }
+ NSString *path = [[self storagePath] stringByAppendingPathComponent:([request cacheStoragePolicy] == ASICacheForSessionDurationCacheStoragePolicy ? sessionCacheFolder : permanentCacheFolder)];
+ path = [path stringByAppendingPathComponent:[[[self class] keyForURL:[request url]] stringByAppendingPathExtension:@"cachedheaders"]];
+ [[self accessLock] unlock];
+ return path;
+}
+
+
- (void)removeCachedDataForRequest:(ASIHTTPRequest *)request
{
[[self accessLock] lock];
if (![self storagePath]) {
[[self accessLock] unlock];
return;
}
- NSString *cachedHeadersPath = [[self storagePath] stringByAppendingPathComponent:[[[self class] keyForRequest:request] stringByAppendingPathExtension:@"cachedheaders"]];
+ NSString *cachedHeadersPath = [self pathToCachedResponseHeadersForURL:[request url]];
if (!cachedHeadersPath) {
[[self accessLock] unlock];
return;
}
- NSString *dataPath = [self pathToCachedResponseDataForRequest:request];
+ NSString *dataPath = [self pathToCachedResponseDataForURL:[request url]];
if (!dataPath) {
[[self accessLock] unlock];
return;
@@ -214,12 +243,12 @@ - (BOOL)isCachedDataCurrentForRequest:(ASIHTTPRequest *)request
[[self accessLock] unlock];
return NO;
}
- NSDictionary *cachedHeaders = [self cachedHeadersForRequest:request];
+ NSDictionary *cachedHeaders = [self cachedResponseHeadersForURL:[request url]];
if (!cachedHeaders) {
[[self accessLock] unlock];
return NO;
}
- NSString *dataPath = [self pathToCachedResponseDataForRequest:request];
+ NSString *dataPath = [self pathToCachedResponseDataForURL:[request url]];
if (!dataPath) {
[[self accessLock] unlock];
return NO;
@@ -306,12 +335,8 @@ - (void)clearCachedResponsesForStoragePolicy:(ASICacheStoragePolicy)storagePolic
[[self accessLock] unlock];
return;
}
- NSString *path;
- if (storagePolicy == ASICachePermanentlyCacheStoragePolicy) {
- path = [[self storagePath] stringByAppendingPathComponent:permanentCacheFolder];
- } else {
- path = [[self storagePath] stringByAppendingPathComponent:sessionCacheFolder];
- }
+ NSString *path = [[self storagePath] stringByAppendingPathComponent:(storagePolicy == ASICacheForSessionDurationCacheStoragePolicy ? sessionCacheFolder : permanentCacheFolder)];
+
BOOL isDirectory = NO;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory];
if (exists && !isDirectory || !exists) {
@@ -355,9 +380,9 @@ + (BOOL)serverAllowsResponseCachingForRequest:(ASIHTTPRequest *)request
}
// Borrowed from: http://stackoverflow.com/questions/652300/using-md5-hash-on-a-string-in-cocoa
-+ (NSString *)keyForRequest:(ASIHTTPRequest *)request
++ (NSString *)keyForURL:(NSURL *)url
{
- const char *cStr = [[[request url] absoluteString] UTF8String];
+ const char *cStr = [[url absoluteString] UTF8String];
unsigned char result[16];
CC_MD5(cStr, (CC_LONG)strlen(cStr), result);
return [NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],result[8], result[9], result[10], result[11],result[12], result[13], result[14], result[15]];
@@ -389,11 +414,11 @@ - (BOOL)canUseCachedDataForRequest:(ASIHTTPRequest *)request
return YES;
}
- NSDictionary *headers = [self cachedHeadersForRequest:request];
+ NSDictionary *headers = [self cachedResponseHeadersForURL:[request url]];
if (!headers) {
return NO;
}
- NSString *dataPath = [self pathToCachedResponseDataForRequest:request];
+ NSString *dataPath = [self pathToCachedResponseDataForURL:[request url]];
if (!dataPath) {
return NO;
}
@@ -421,6 +446,23 @@ - (BOOL)canUseCachedDataForRequest:(ASIHTTPRequest *)request
return NO;
}
+/*
+NSURLCache compatibility
+*/
+- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
+{
+ NSData *data = [self cachedResponseDataForURL:[request URL]];
+ NSDictionary *headers = [self cachedResponseHeadersForURL:[request URL]];
+ if (!data || !headers) {
+ return [super cachedResponseForRequest:request];
+ }
+ NSString *mimeType = nil;
+ NSStringEncoding charset;
+ [ASIHTTPRequest parseMimeType:&mimeType andResponseEncoding:&charset fromContentType:[headers objectForKey:@"Content-Type"]];
+ NSURLResponse *urlResponse = [[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:mimeType expectedContentLength:[data length] textEncodingName:nil] autorelease];
+ return [[NSCachedURLResponse alloc] initWithResponse:urlResponse data:data];
+}
+
@synthesize storagePath;
@synthesize defaultCachePolicy;
@synthesize accessLock;
View
@@ -590,6 +590,8 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
// Attempts to set the correct encoding by looking at the Content-Type header, if this is one
- (void)parseStringEncodingFromHeaders;
++ (void)parseMimeType:(NSString **)mimeType andResponseEncoding:(NSStringEncoding *)stringEncoding fromContentType:(NSString *)contentType;
+
#pragma mark http authentication stuff
// Apply credentials to this request
@@ -793,6 +795,7 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
// Returns a date from a string in RFC1123 format
+ (NSDate *)dateFromRFC1123String:(NSString *)string;
+
// Used for detecting multitasking support at runtime (for backgrounding requests)
#if TARGET_OS_IPHONE
+ (BOOL)isMultitaskingSupported;
Oops, something went wrong.

0 comments on commit b152a3d

Please sign in to comment.