Ben Copsey

Parse expires / max-age headers

@@ -109,6 +109,8 @@ static NSString *permanentCacheFolder = @"PermanentStore"; @@ -109,6 +109,8 @@ static NSString *permanentCacheFolder = @"PermanentStore";
109 if ([request isResponseCompressed]) { 109 if ([request isResponseCompressed]) {
110 [responseHeaders removeObjectForKey:@"Content-Encoding"]; 110 [responseHeaders removeObjectForKey:@"Content-Encoding"];
111 } 111 }
  112 + // We use this special key to help expire the request when we get a max-age header
  113 + [responseHeaders setObject:[NSDate date] forKey:@"X-ASIHTTPRequest-Fetch-date"];
112 [responseHeaders writeToFile:metadataPath atomically:NO]; 114 [responseHeaders writeToFile:metadataPath atomically:NO];
113 115
114 if ([request responseData]) { 116 if ([request responseData]) {
@@ -182,12 +184,38 @@ static NSString *permanentCacheFolder = @"PermanentStore"; @@ -182,12 +184,38 @@ static NSString *permanentCacheFolder = @"PermanentStore";
182 if (!dataPath) { 184 if (!dataPath) {
183 return NO; 185 return NO;
184 } 186 }
  187 + // If the Etag or Last-Modified date are different from the one we have, fetch the document again
185 NSArray *headersToCompare = [NSArray arrayWithObjects:@"etag",@"last-modified",nil]; 188 NSArray *headersToCompare = [NSArray arrayWithObjects:@"etag",@"last-modified",nil];
186 for (NSString *header in headersToCompare) { 189 for (NSString *header in headersToCompare) {
187 if (![[[self class] responseHeader:header fromHeaders:[request responseHeaders]] isEqualToString:[[self class] responseHeader:header fromHeaders:cachedHeaders]]) { 190 if (![[[self class] responseHeader:header fromHeaders:[request responseHeaders]] isEqualToString:[[self class] responseHeader:header fromHeaders:cachedHeaders]]) {
188 return NO; 191 return NO;
189 } 192 }
190 } 193 }
  194 + if (![self shouldRespectCacheControlHeaders]) {
  195 + return YES;
  196 + }
  197 + // Look for an Expires header to see if the content is out of data
  198 + NSString *expires = [[self class] responseHeader:@"expires" fromHeaders:cachedHeaders];
  199 + if (expires) {
  200 + if ([[ASIHTTPRequest dateFromRFC1123String:expires] timeIntervalSinceNow] < 0) {
  201 + return NO;
  202 + }
  203 + }
  204 + // Look for a max-age header
  205 + NSString *cacheControl = [[[self class] responseHeader:@"cache-control" fromHeaders:cachedHeaders] lowercaseString];
  206 + if (cacheControl) {
  207 + NSScanner *scanner = [NSScanner scannerWithString:cacheControl];
  208 + if ([scanner scanString:@"max-age" intoString:NULL]) {
  209 + [scanner scanString:@"=" intoString:NULL];
  210 + NSTimeInterval maxAge = 0;
  211 + [scanner scanDouble:&maxAge];
  212 + NSDate *fetchDate = [[cachedHeaders objectForKey:@"X-ASIHTTPRequest-Fetch-date"] dateValue];
  213 + NSDate *expiryDate = [fetchDate addTimeInterval:maxAge];
  214 + if ([expiryDate timeIntervalSinceNow] < 0) {
  215 + return NO;
  216 + }
  217 + }
  218 + }
191 return YES; 219 return YES;
192 } 220 }
193 221
@@ -702,6 +702,9 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -702,6 +702,9 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
702 // And also by ASIS3Request 702 // And also by ASIS3Request
703 + (NSString *)base64forData:(NSData *)theData; 703 + (NSString *)base64forData:(NSData *)theData;
704 704
  705 +// Returns a date from a string in RFC1123 format
  706 ++ (NSDate *)dateFromRFC1123String:(NSString *)string;
  707 +
705 #pragma mark === 708 #pragma mark ===
706 709
707 @property (retain) NSString *username; 710 @property (retain) NSString *username;
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 23
24 24
25 // Automatically set on build 25 // Automatically set on build
26 -NSString *ASIHTTPRequestVersion = @"v1.6.2-12 2010-05-03"; 26 +NSString *ASIHTTPRequestVersion = @"v1.6.2-13 2010-05-03";
27 27
28 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; 28 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
29 29
@@ -3780,6 +3780,25 @@ static id <ASICacheDelegate> defaultCache = nil; @@ -3780,6 +3780,25 @@ static id <ASICacheDelegate> defaultCache = nil;
3780 return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]; 3780 return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
3781 } 3781 }
3782 3782
  3783 +// Based on hints from http://stackoverflow.com/questions/1850824/parsing-a-rfc-822-date-with-nsdateformatter
  3784 ++ (NSDate *)dateFromRFC1123String:(NSString *)string
  3785 +{
  3786 + NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
  3787 + [formatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]];
  3788 + // Does the string include a week day?
  3789 + NSString *day = @"";
  3790 + if ([string rangeOfString:@","].location != NSNotFound) {
  3791 + day = @"EEE, ";
  3792 + }
  3793 + // Does the string include seconds?
  3794 + NSString *seconds = @"";
  3795 + if ([[string componentsSeparatedByString:@":"] count] == 3) {
  3796 + seconds = @":ss";
  3797 + }
  3798 + [formatter setDateFormat:[NSString stringWithFormat:@"%@dd MMM yyyy HH:mm%@ z",day,seconds]];
  3799 + return [formatter dateFromString:string];
  3800 +}
  3801 +
3783 #pragma mark === 3802 #pragma mark ===
3784 3803
3785 @synthesize username; 3804 @synthesize username;
@@ -1579,5 +1579,25 @@ @@ -1579,5 +1579,25 @@
1579 [[self responseData] appendData:data]; 1579 [[self responseData] appendData:data];
1580 } 1580 }
1581 1581
  1582 +- (void)testRFC1123DateParsing
  1583 +{
  1584 + unsigned dateUnits = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit;
  1585 + NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
  1586 + [calendar setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];
  1587 + NSString *dateString = @"Thu, 19 Nov 1981 08:52:01 GMT";
  1588 + NSDate *date = [ASIHTTPRequest dateFromRFC1123String:dateString];
  1589 + NSDateComponents *components = [calendar components:dateUnits fromDate:date];
  1590 + NSLog(@"%i",[components weekday]);
  1591 + BOOL success = ([components year] == 1981 && [components month] == 11 && [components day] == 19 && [components weekday] == 5 && [components hour] == 8 && [components minute] == 52 && [components second] == 1);
  1592 + GHAssertTrue(success,@"Failed to parse an RFC1123 date correctly");
  1593 +
  1594 + dateString = @"4 May 2010 00:59 CET";
  1595 + date = [ASIHTTPRequest dateFromRFC1123String:dateString];
  1596 + components = [calendar components:dateUnits fromDate:date];
  1597 + success = ([components year] == 2010 && [components month] == 5 && [components day] == 3 && [components hour] == 23 && [components minute] == 59);
  1598 + GHAssertTrue(success,@"Failed to parse an RFC1123 date correctly");
  1599 +
  1600 +}
  1601 +
1582 @synthesize responseData; 1602 @synthesize responseData;
1583 @end 1603 @end
This diff was suppressed by a .gitattributes entry.