3
votes

I'm using the following code to download some data from TfL.

NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                                      delegate:self delegateQueue:[NSOperationQueue mainQueue]];

NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.tfl.gov.uk/StopPoint/490012211N/Arrivals"]];

[[session dataTaskWithRequest:req
        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (!error) {
                NSLog(@"Loaded Succesfully");
            }
            NSCachedURLResponse *resp = [[NSURLCache sharedURLCache] cachedResponseForRequest:req];
            if (resp) {
                NSLog(@"Response in cache");
            }
            [[session dataTaskWithRequest:req
                        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                            if (!error) {
                                NSLog(@"Loaded Sucessfully again");
                            }
                        }] resume];
        }] resume];

The reponse's cache-control header field is public, must-revalidate, max-age=5, s-maxage=10 This seems to mean that the server will sent a request even if the second requests are within the 5 seconds given by the max-age. If I use Charles to edit the response and remove the must-revalidate, then the code above only send a request to the server once.

The HTTP/1.1 spec says

that cache MUST NOT use the entry after it becomes stale to respond to a subsequent request without first revalidating it with the origin server.

for must-revalidate, however it seems iOS seems to ignore the "after it become stale" part? and takes must-revalidate to mean ignore max-age. How is then different to setting the field to not cache at all. I've checked and the request/response is being added to the cache, so it seems must-revalidate just bloats the cache as it will not ever use it, but still adds it to the cache?

So is the server incorrect for having must-revalidate in conjunction with a max-age or is iOS incorrect in the way it handles must-revalidate?

Is there a way of intercepting the response such that I can remove the must-revalidate? NSURLProtocol seems like it could be useful but I can only see how to modify requests, not responses.

1

1 Answers

0
votes

If you use NSURLProtocol, you can issue a new request (be sure to tag it so that you won't modify it a second time), and then on the response, you can create a new response object based on the old one, and send that modified response to the client. Whether that's sufficient to get around your issue or not, I'm not sure.

However, a better (read "much more straightforward to implement") solution is probably to subclass the NSURLCache object and use your custom subclass either as the shared cache (for NSURLConnection and the NSURLSession shared session) or as a per-session cache (for other NSURLSession sessions).