Skip to content
Browse files

Important changes to download cache behaviour:

ASIDownloadCache will now store 301, 302, 303 and 307 redirect responses in the cache. Requests pulling from the cache will automatically redirect (assuming shouldRedirect is true) as if they'd received a Location header from the server.
This change should allow the download cache to operate more effectively as an offline fallback when no internet connection is available.
Additionally, requests encountering a 304 will no longer read from the cache and then write back to it. Instead, they merely update the expiry date (if an updated date was supplied).
As part of this work, two new required methods were added to the ASICacheDelegate protocol, see ASICacheDelegate.h for more info
Finally, ASIDownloadCache now stores the response status code in a custom header in the cached headers dictionary, and requests will have their response status code set to this when they are pulled from the cache.
  • Loading branch information...
1 parent ad73765 commit e76a84533e9a985df5e83588b5de58336766651b @pokeb pokeb committed May 15, 2011
View
7 Classes/ASICacheDelegate.h
@@ -56,6 +56,13 @@ typedef enum _ASICacheStoragePolicy {
// Should return the cache policy that will be used when requests have their cache policy set to ASIUseDefaultCachePolicy
- (ASICachePolicy)defaultCachePolicy;
+// Returns the date a cached response should expire on. Pass a non-zero max age to specify a custom date.
+- (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge;
+
+// Updates cached response headers with a new expiry date. Pass a non-zero max age to specify a custom date.
+- (void)updateExpiryForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge;
+
+// Looks at the request's cache policy and any cached headers to determine if the cache data is still valid
- (BOOL)canUseCachedDataForRequest:(ASIHTTPRequest *)request;
// Removes cached data for a particular request
View
99 Classes/ASIDownloadCache.m
@@ -85,31 +85,24 @@ - (void)setStoragePath:(NSString *)path
[[self accessLock] unlock];
}
-- (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge
+- (void)updateExpiryForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge
{
- [[self accessLock] lock];
-
- if ([request error] || ![request responseHeaders] || ([request responseStatusCode] != 200) || ([request cachePolicy] & ASIDoNotWriteToCacheCachePolicy)) {
- [[self accessLock] unlock];
+ NSString *headerPath = [self pathToStoreCachedResponseHeadersForRequest:request];
+ NSMutableDictionary *cachedHeaders = [NSMutableDictionary dictionaryWithContentsOfFile:headerPath];
+ if (!cachedHeaders) {
return;
}
-
- if ([self shouldRespectCacheControlHeaders] && ![[self class] serverAllowsResponseCachingForRequest:request]) {
- [[self accessLock] unlock];
+ NSDate *expires = [self expiryDateForRequest:request maxAge:maxAge];
+ if (!expires) {
return;
}
+ [cachedHeaders setObject:[NSNumber numberWithDouble:[expires timeIntervalSince1970]] forKey:@"X-ASIHTTPRequest-Expires"];
+ [cachedHeaders writeToFile:headerPath atomically:NO];
+}
- NSString *headerPath = [self pathToStoreCachedResponseHeadersForRequest:request];
- NSString *dataPath = [self pathToStoreCachedResponseDataForRequest:request];
-
+- (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge
+{
NSMutableDictionary *responseHeaders = [NSMutableDictionary dictionaryWithDictionary:[request responseHeaders]];
- if ([request isResponseCompressed]) {
- [responseHeaders removeObjectForKey:@"Content-Encoding"];
- }
-
- // Create a special 'X-ASIHTTPRequest-Expires' header
- // This is what we use for deciding if cached data is current, rather than parsing the expires / max-age headers individually each time
- // We store this as a timestamp to make reading it easier as NSDateFormatter is quite expensive
// If we weren't given a custom max-age, lets look for one in the response headers
if (!maxAge) {
@@ -126,16 +119,65 @@ - (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval
// RFC 2612 says max-age must override any Expires header
if (maxAge) {
- [responseHeaders setObject:[NSNumber numberWithDouble:[[[NSDate date] addTimeInterval:maxAge] timeIntervalSince1970]] forKey:@"X-ASIHTTPRequest-Expires"];
+ return [[NSDate date] addTimeInterval:maxAge];
} else {
NSString *expires = [responseHeaders objectForKey:@"Expires"];
if (expires) {
- [responseHeaders setObject:[NSNumber numberWithDouble:[[ASIHTTPRequest dateFromRFC1123String:expires] timeIntervalSince1970]] forKey:@"X-ASIHTTPRequest-Expires"];
+ return [ASIHTTPRequest dateFromRFC1123String:expires];
}
}
+ return nil;
+}
+
+- (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge
+{
+ [[self accessLock] lock];
+
+ if ([request error] || ![request responseHeaders] || ([request cachePolicy] & ASIDoNotWriteToCacheCachePolicy)) {
+ [[self accessLock] unlock];
+ return;
+ }
+
+ // We only cache 200/OK or redirect reponses (redirect responses are cached so the cache works better with no internet connection)
+ int responseCode = [request responseStatusCode];
+ if (responseCode != 200 && responseCode != 301 && responseCode != 302 && responseCode != 303 && responseCode != 307) {
+ [[self accessLock] unlock];
+ return;
+ }
+
+ if ([self shouldRespectCacheControlHeaders] && ![[self class] serverAllowsResponseCachingForRequest:request]) {
+ [[self accessLock] unlock];
+ return;
+ }
+
+ NSString *headerPath = [self pathToStoreCachedResponseHeadersForRequest:request];
+ NSString *dataPath = [self pathToStoreCachedResponseDataForRequest:request];
+
+ NSMutableDictionary *responseHeaders = [NSMutableDictionary dictionaryWithDictionary:[request responseHeaders]];
+ if ([request isResponseCompressed]) {
+ [responseHeaders removeObjectForKey:@"Content-Encoding"];
+ }
+
+ // Create a special 'X-ASIHTTPRequest-Expires' header
+ // This is what we use for deciding if cached data is current, rather than parsing the expires / max-age headers individually each time
+ // We store this as a timestamp to make reading it easier as NSDateFormatter is quite expensive
+
+ NSDate *expires = [self expiryDateForRequest:request maxAge:maxAge];
+ if (expires) {
+ [responseHeaders setObject:[NSNumber numberWithDouble:[expires timeIntervalSince1970]] forKey:@"X-ASIHTTPRequest-Expires"];
+ }
+
+ // Store the response code in a custom header so we can reuse it later
+
+ // We'll change 304/Not Modified to 200/OK because this is likely to be us updating the cached headers with a conditional GET
+ int statusCode = [request responseStatusCode];
+ if (statusCode == 304) {
+ statusCode = 200;
+ }
+ [responseHeaders setObject:[NSNumber numberWithInt:[request responseStatusCode]] forKey:@"X-ASIHTTPRequest-Response-Status-Code"];
[responseHeaders writeToFile:headerPath atomically:NO];
-
+
if ([request responseData]) {
[[request responseData] writeToFile:dataPath atomically:NO];
} else if ([request downloadDestinationPath] && ![[request downloadDestinationPath] isEqualToString:dataPath]) {
@@ -282,14 +324,15 @@ - (BOOL)isCachedDataCurrentForRequest:(ASIHTTPRequest *)request
return NO;
}
- // If we already have response headers for this request, check to see if the new content is different
- if ([request responseHeaders]) {
+ // New content is not different
+ if ([request responseStatusCode] == 304) {
+ [[self accessLock] unlock];
+ return YES;
+ }
- // New content is not different
- if ([request responseStatusCode] == 304) {
- [[self accessLock] unlock];
- return YES;
- }
+ // If we already have response headers for this request, check to see if the new content is different
+ // We check [request complete] so that we don't end up comparing response headers from a redirection with these
+ if ([request responseHeaders] && [request complete]) {
// If the Etag or Last-Modified date are different from the one we have, we'll have to fetch this resource again
NSArray *headersToCompare = [NSArray arrayWithObjects:@"Etag",@"Last-Modified",nil];
View
191 Classes/ASIHTTPRequest.m
@@ -24,7 +24,7 @@
#import "ASIDataCompressor.h"
// Automatically set on build
-NSString *ASIHTTPRequestVersion = @"v1.8-84 2011-05-15";
+NSString *ASIHTTPRequestVersion = @"v1.8-85 2011-05-15";
static NSString *defaultUserAgent = nil;
@@ -163,6 +163,8 @@ - (void)reportFinished;
- (void)markAsFinished;
- (void)performRedirect;
- (BOOL)shouldTimeOut;
+- (BOOL)willRedirect;
+- (BOOL)willAskDelegateToConfirmRedirect;
+ (void)performInvocation:(NSInvocation *)invocation onTarget:(id *)target releasingObject:(id)objectToRelease;
+ (void)hideNetworkActivityIndicatorAfterDelay;
@@ -2123,19 +2125,18 @@ - (void)readResponseHeaders
}
#endif
- CFDictionaryRef headerFields = CFHTTPMessageCopyAllHeaderFields(message);
- [self setResponseHeaders:(NSDictionary *)headerFields];
-
- CFRelease(headerFields);
-
+ [self setResponseHeaders:[(NSDictionary *)CFHTTPMessageCopyAllHeaderFields(message) autorelease]];
[self setResponseStatusCode:(int)CFHTTPMessageGetResponseStatusCode(message)];
[self setResponseStatusMessage:[(NSString *)CFHTTPMessageCopyResponseStatusLine(message) autorelease]];
-
+
if ([self downloadCache] && ([[self downloadCache] canUseCachedDataForRequest:self])) {
+
+ // Update the expiry date
+ [[self downloadCache] updateExpiryForRequest:self maxAge:[self secondsToCache]];
+
// Read the response from the cache
[self useDataFromCache];
- // Update the response headers (this will usually move the expiry date into the future)
- [[self downloadCache] storeResponseForRequest:self maxAge:[self secondsToCache]];
+
CFRelease(message);
return;
}
@@ -2146,11 +2147,11 @@ - (void)readResponseHeaders
} else if ([self responseStatusCode] == 407) {
[self setAuthenticationNeeded:ASIProxyAuthenticationNeeded];
} else {
- #if DEBUG_HTTP_AUTHENTICATION
+ #if DEBUG_HTTP_AUTHENTICATION
if ([self authenticationScheme]) {
NSLog(@"[AUTH] Request %@ has passed %@ authentication",self,[self authenticationScheme]);
}
- #endif
+ #endif
}
// Authentication succeeded, or no authentication was required
@@ -2196,57 +2197,7 @@ - (void)readResponseHeaders
}
// Do we need to redirect?
- // Note that ASIHTTPRequest does not currently support 305 Use Proxy
- if ([self shouldRedirect] && [responseHeaders valueForKey:@"Location"]) {
- if (([self responseStatusCode] > 300 && [self responseStatusCode] < 304) || [self responseStatusCode] == 307) {
-
- [self performSelectorOnMainThread:@selector(requestRedirected) withObject:nil waitUntilDone:[NSThread isMainThread]];
-
- // By default, we redirect 301 and 302 response codes as GET requests
- // According to RFC 2616 this is wrong, but this is what most browsers do, so it's probably what you're expecting to happen
- // See also:
- // http://allseeing-i.lighthouseapp.com/projects/27881/tickets/27-302-redirection-issue
-
- if ([self responseStatusCode] != 307 && (![self shouldUseRFC2616RedirectBehaviour] || [self responseStatusCode] == 303)) {
- [self setRequestMethod:@"GET"];
- [self setPostBody:nil];
- [self setPostLength:0];
-
- // Perhaps there are other headers we should be preserving, but it's hard to know what we need to keep and what to throw away.
- NSString *userAgentHeader = [[self requestHeaders] objectForKey:@"User-Agent"];
- NSString *acceptHeader = [[self requestHeaders] objectForKey:@"Accept"];
- [self setRequestHeaders:nil];
- if (userAgentHeader) {
- [self addRequestHeader:@"User-Agent" value:userAgentHeader];
- }
- if (acceptHeader) {
- [self addRequestHeader:@"Accept" value:acceptHeader];
- }
- [self setHaveBuiltRequestHeaders:NO];
- } else {
-
- // Force rebuild the cookie header incase we got some new cookies from this request
- // All other request headers will remain as they are for 301 / 302 redirects
- [self applyCookieHeader];
- }
-
- // Force the redirected request to rebuild the request headers (if not a 303, it will re-use old ones, and add any new ones)
- [self setRedirectURL:[[NSURL URLWithString:[responseHeaders valueForKey:@"Location"] relativeToURL:[self url]] absoluteURL]];
- [self setNeedsRedirect:YES];
-
- // Clear the request cookies
- // This means manually added cookies will not be added to the redirect request - only those stored in the global persistent store
- // But, this is probably the safest option - we might be redirecting to a different domain
- [self setRequestCookies:[NSMutableArray array]];
-
- #if DEBUG_REQUEST_STATUS
- NSLog(@"[STATUS] Request will redirect (code: %i): %@",[self responseStatusCode],self);
- #endif
-
- }
- }
-
- if (![self needsRedirect]) {
+ if (![self willRedirect]) {
// See if we got a Content-length header
NSString *cLength = [responseHeaders valueForKey:@"Content-Length"];
ASIHTTPRequest *theRequest = self;
@@ -2279,6 +2230,7 @@ - (void)readResponseHeaders
if ([self shouldAttemptPersistentConnection]) {
NSString *connectionHeader = [[[self responseHeaders] objectForKey:@"Connection"] lowercaseString];
+
NSString *httpVersion = NSMakeCollectable([(NSString *)CFHTTPMessageCopyVersion(message) autorelease]);
// Don't re-use the connection if the server is HTTP 1.0 and didn't send Connection: Keep-Alive
@@ -2322,6 +2274,65 @@ - (void)readResponseHeaders
[self performSelectorOnMainThread:@selector(requestReceivedResponseHeaders:) withObject:[[[self responseHeaders] copy] autorelease] waitUntilDone:[NSThread isMainThread]];
}
+- (BOOL)willRedirect
+{
+ // Do we need to redirect?
+ if (![self shouldRedirect] || ![responseHeaders valueForKey:@"Location"]) {
+ return NO;
+ }
+
+ // Note that ASIHTTPRequest does not currently support 305 Use Proxy
+ int responseCode = [self responseStatusCode];
+ if (responseCode != 301 && responseCode != 302 && responseCode != 303 && responseCode != 307) {
+ return NO;
+ }
+
+ [self performSelectorOnMainThread:@selector(requestRedirected) withObject:nil waitUntilDone:[NSThread isMainThread]];
+
+ // By default, we redirect 301 and 302 response codes as GET requests
+ // According to RFC 2616 this is wrong, but this is what most browsers do, so it's probably what you're expecting to happen
+ // See also:
+ // http://allseeing-i.lighthouseapp.com/projects/27881/tickets/27-302-redirection-issue
+
+ if (responseCode != 307 && (![self shouldUseRFC2616RedirectBehaviour] || responseCode == 303)) {
+ [self setRequestMethod:@"GET"];
+ [self setPostBody:nil];
+ [self setPostLength:0];
+
+ // Perhaps there are other headers we should be preserving, but it's hard to know what we need to keep and what to throw away.
+ NSString *userAgentHeader = [[self requestHeaders] objectForKey:@"User-Agent"];
+ NSString *acceptHeader = [[self requestHeaders] objectForKey:@"Accept"];
+ [self setRequestHeaders:nil];
+ if (userAgentHeader) {
+ [self addRequestHeader:@"User-Agent" value:userAgentHeader];
+ }
+ if (acceptHeader) {
+ [self addRequestHeader:@"Accept" value:acceptHeader];
+ }
+ [self setHaveBuiltRequestHeaders:NO];
+
+ } else {
+ // Force rebuild the cookie header incase we got some new cookies from this request
+ // All other request headers will remain as they are for 301 / 302 redirects
+ [self applyCookieHeader];
+ }
+
+ // Force the redirected request to rebuild the request headers (if not a 303, it will re-use old ones, and add any new ones)
+ [self setRedirectURL:[[NSURL URLWithString:[responseHeaders valueForKey:@"Location"] relativeToURL:[self url]] absoluteURL]];
+ [self setNeedsRedirect:YES];
+
+ // Clear the request cookies
+ // This means manually added cookies will not be added to the redirect request - only those stored in the global persistent store
+ // But, this is probably the safest option - we might be redirecting to a different domain
+ [self setRequestCookies:[NSMutableArray array]];
+
+ #if DEBUG_REQUEST_STATUS
+ NSLog(@"[STATUS] Request will redirect (code: %i): %@",responseCode,self);
+ #endif
+
+ return YES;
+}
+
- (void)parseStringEncodingFromHeaders
{
// Handle response text encoding
@@ -3155,22 +3166,7 @@ - (void)handleNetworkEvent:(CFStreamEventType)type
[[self cancelledLock] unlock];
if ([self downloadComplete] && [self needsRedirect]) {
-
- // We must lock again to ensure delegate / queue aren't changed while we check them
- [[self cancelledLock] lock];
- // Here we perform an initial check to see if either the delegate or the queue wants to be asked about the redirect, because if not we should redirect straight away
- // We will check again on the main thread later
- BOOL needToAskDelegateAboutRedirect = (([self delegate] && [[self delegate] respondsToSelector:[self willRedirectSelector]]) || ([self queue] && [[self queue] respondsToSelector:@selector(request:willRedirectToURL:)]));
- [[self cancelledLock] unlock];
-
- // Either the delegate or the queue's delegate is interested in being told when we are about to redirect
- if (needToAskDelegateAboutRedirect) {
- NSURL *newURL = [[[self redirectURL] copy] autorelease];
- [self setRedirectURL:nil];
- [self performSelectorOnMainThread:@selector(requestWillRedirectToURL:) withObject:newURL waitUntilDone:[NSThread isMainThread]];
-
- // If neither the delegate nor the queue's delegate implement request:willRedirectToURL:, we will redirect automatically
- } else {
+ if (![self willAskDelegateToConfirmRedirect]) {
[self performRedirect];
}
} else if ([self downloadComplete] && [self authenticationNeeded]) {
@@ -3181,6 +3177,27 @@ - (void)handleNetworkEvent:(CFStreamEventType)type
[pool release];
}
+- (BOOL)willAskDelegateToConfirmRedirect
+{
+ // We must lock to ensure delegate / queue aren't changed while we check them
+ [[self cancelledLock] lock];
+
+ // Here we perform an initial check to see if either the delegate or the queue wants to be asked about the redirect, because if not we should redirect straight away
+ // We will check again on the main thread later
+ BOOL needToAskDelegateAboutRedirect = (([self delegate] && [[self delegate] respondsToSelector:[self willRedirectSelector]]) || ([self queue] && [[self queue] respondsToSelector:@selector(request:willRedirectToURL:)]));
+
+ [[self cancelledLock] unlock];
+
+ // Either the delegate or the queue's delegate is interested in being told when we are about to redirect
+ if (needToAskDelegateAboutRedirect) {
+ NSURL *newURL = [[[self redirectURL] copy] autorelease];
+ [self setRedirectURL:nil];
+ [self performSelectorOnMainThread:@selector(requestWillRedirectToURL:) withObject:newURL waitUntilDone:[NSThread isMainThread]];
+ return true;
+ }
+ return false;
+}
+
- (void)handleBytesAvailable
{
if (![self responseHeaders]) {
@@ -3520,13 +3537,10 @@ - (void)useDataFromCache
if (headers && dataPath) {
- // only 200 responses are stored in the cache, so let the client know
- // this was a successful response
- [self setResponseStatusCode:200];
-
+ [self setResponseStatusCode:[[headers objectForKey:@"X-ASIHTTPRequest-Response-Status-Code"] intValue]];
[self setDidUseCachedResponse:YES];
-
[theRequest setResponseHeaders:headers];
+
if ([theRequest downloadDestinationPath]) {
[theRequest setDownloadDestinationPath:dataPath];
} else {
@@ -3538,10 +3552,19 @@ - (void)useDataFromCache
[theRequest parseStringEncodingFromHeaders];
[theRequest setResponseCookies:[NSHTTPCookie cookiesWithResponseHeaderFields:headers forURL:[self url]]];
+
+ // See if we need to redirect
+ if ([self willRedirect]) {
+ if (![self willAskDelegateToConfirmRedirect]) {
+ [self performRedirect];
+ }
+ return;
+ }
}
+
[theRequest setComplete:YES];
[theRequest setDownloadComplete:YES];
-
+
// If we're pulling data from the cache without contacting the server at all, we won't have set originalURL yet
if ([self redirectCount] == 0) {
[theRequest setOriginalURL:[theRequest url]];
View
1 Classes/Tests/ASIDownloadCacheTests.h
@@ -11,6 +11,7 @@
@interface ASIDownloadCacheTests : ASITestCase {
NSUInteger requestsFinishedCount;
+ BOOL requestRedirectedWasCalled;
}
@end
View
77 Classes/Tests/ASIDownloadCacheTests.m
@@ -14,6 +14,7 @@
@interface ASIDownloadCacheTests ()
- (void)runCacheOnlyCallsRequestFinishedOnceTest;
- (void)finishCached:(ASIHTTPRequest *)request;
+- (void)runRedirectTest;
@end
@@ -330,14 +331,16 @@ - (void)testCustomExpiry
- (void)test304
{
+ NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"];
+
// Test default cache policy
[[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
[[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIUseDefaultCachePolicy];
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]];
+ ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadCache:[ASIDownloadCache sharedCache]];
[request startSynchronous];
- request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]];
+ request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadCache:[ASIDownloadCache sharedCache]];
[request startSynchronous];
BOOL success = ([request responseStatusCode] == 200);
@@ -348,6 +351,30 @@ - (void)test304
success = ([[request responseData] length]);
GHAssertTrue(success,@"Response was empty");
+
+ // Test 304 updates expiry date
+ url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/content_not_modified_but_expires_tomorrow"];
+ request = [ASIHTTPRequest requestWithURL:url];
+ [request setDownloadCache:[ASIDownloadCache sharedCache]];
+ [request startSynchronous];
+
+ NSTimeInterval expiryTimestamp = [[[[ASIDownloadCache sharedCache] cachedResponseHeadersForURL:url] objectForKey:@"X-ASIHTTPRequest-Expires"] doubleValue];
+
+ // Wait to give the expiry date a chance to change
+ sleep(2);
+
+ request = [ASIHTTPRequest requestWithURL:url];
+ [request setCachePolicy:ASIAskServerIfModifiedCachePolicy];
+ [request setDownloadCache:[ASIDownloadCache sharedCache]];
+ [request startSynchronous];
+
+ success = [request didUseCachedResponse];
+ GHAssertTrue(success, @"Cached data should have been used");
+
+ NSTimeInterval newExpiryTimestamp = [[[[ASIDownloadCache sharedCache] cachedResponseHeadersForURL:url] objectForKey:@"X-ASIHTTPRequest-Expires"] doubleValue];
+ NSLog(@"%@",[request responseString]);
+ success = (newExpiryTimestamp > expiryTimestamp);
+ GHAssertTrue(success, @"Failed to update expiry timestamp on 304");
}
- (void)testStringEncoding
@@ -433,4 +460,50 @@ - (void)finishCached:(ASIHTTPRequest *)request
requestsFinishedCount++;
}
+- (void)testRedirect
+{
+ // Run this request on the main thread to force delegate calls to happen synchronously
+ [self performSelectorOnMainThread:@selector(runRedirectTest) withObject:nil waitUntilDone:YES];
+}
+
+- (void)runRedirectTest
+{
+ [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
+ [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
+ [[ASIDownloadCache sharedCache] setDefaultCachePolicy:ASIUseDefaultCachePolicy];
+ [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]];
+
+ ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cached-redirect"]];
+ [request startSynchronous];
+
+ BOOL success = ([[[request url] absoluteString] isEqualToString:@"http://allseeing-i.com/i/logo.png"]);
+ GHAssertTrue(success,@"Request did not redirect correctly, cannot proceed with test");
+
+ requestRedirectedWasCalled = NO;
+ request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/cached-redirect"]];
+ [request setDelegate:self];
+ [request startSynchronous];
+
+ success = ([request didUseCachedResponse]);
+ GHAssertTrue(success,@"Failed to cache final response");
+
+ GHAssertTrue(requestRedirectedWasCalled,@"Failed to call requestRedirected");
+}
+
+- (void)requestRedirected:(ASIHTTPRequest *)redirected
+{
+ requestRedirectedWasCalled = YES;
+}
+
+- (void)request:(ASIHTTPRequest *)request willRedirectToURL:(NSURL *)newURL
+{
+ BOOL success = ([[newURL absoluteString] isEqualToString:@"http://allseeing-i.com/i/logo.png"]);
+ GHAssertTrue(success,@"Request did not redirect correctly, cannot proceed with test");
+
+ success = ([request didUseCachedResponse]);
+ GHAssertTrue(success,@"Failed to cache redirect response");
+
+ [request redirectToURL:newURL];
+}
+
@end

0 comments on commit e76a845

Please sign in to comment.
Something went wrong with that request. Please try again.