I'm using AFNetworking, making API calls that respond with proper cache-control headers.
Everything works fine with requests honoring the cache except for one glitch.
The API I'm using requires a signature to be created with a lifetime of 5 minutes. It's generated from the current time, API key, and API secret. So when I pass this in as a sig parameter, the cache is going to continually miss.
Example:
request1: http://foo.com?p=hello&apikey=12345&sig=ABCDEF
request2: http://foo.com?p=hello&apikey=12345&sig=ZYXWVU
So request 2 is a cache miss.
Question: How could I modify the request in such a way as to strip out the signature parameter for caching only?
Related
According to the documentation if you use the default useProtocolCachePolicy the logic is as follows:
If a cached response does not exist for the request, the URL loading system fetches the data from the originating source.
Otherwise, if the cached response does not indicate that it must be revalidated every time, and if the cached response is not stale (past its expiration date), the URL loading system returns the cached response.
If the cached response is stale or requires revalidation, the URL loading system makes a HEAD request to the originating source to see if the resource has changed. If so, the URL loading system fetches the data from the originating source. Otherwise, it returns the cached response.
However, my experimentation (see below) has shown this to be completely false. Even if the response is cached it is never used, and no HEAD request is ever made.
Scenario
I am making a request to a URL that returns ETag and Last-Modified headers which never change. I have made the request at least once so the response is already cached (which I can verify by looking at the cache DB for the app on the iOS simulator)
Using useProtocolCachePolicy (the default)
If I have a URLSession with a URLSessionConfiguration with requestCachePolicy set to useProtocolCachePolicy then the response is cached (I can see it in the cache DB), but the cached response is never used. Repeated requests to the same URL always make a new GET request without If-None-Match or If-Modified-Since headers, so the server always returns HTTP 200 with the full response. The cached response is ignored.
Using reloadRevalidatingCacheData on every URLRequest
If I set the cachePolicy on each URLRequest to reloadRevalidatingCacheData then I see caching in action. Each time I make the request, a GET request is made with the If-None-Match and If-Modified-Since headers set to the values of the ETag and Last-Modified headers, respectively, of the cached response. As nothing has changed, the server responds with a 304 Not Modified, and the locally cached response is returned to the caller.
Using reloadRevalidatingCacheData only on the URLSessionConfiguration
If I only set requestCachePolicy = . reloadRevalidatingCacheData on the URLSessionConfiguration (instead of on each URLRequest) then when the app starts only the first request uses cache headers and gets a 304 Not Modified response. Subsequent requests are normal GET requests without any cache headers.
Conclusion
All the other cache policy settings are basically variants of "only use cached data" or "never use the cache" so are not relevant here.
There is no scenario in which URLSession makes a HEAD request as the documentation claims, and no situation in which it just uses cached data without revalidation based on expiration date information in the original response.
The workaround I will use is to set cachePolicy = .reloadRevalidatingCacheData on every URLRequest to get some level of local caching, as 304 Not Modified response only return headers and no data so there is a saving of network traffic.
If anyone as any better solutions, or knows how to get URLSession working as documented, then I would love to know.
Service response headers should include:
Cache-Control: must-revalidate
Apple will use this instruction to implement .useProtocolCachePolicy as described in documentation.
Hi i have read about cache policy but still not quite clear. My purpose is to set request cache 3 minutes. Response cache 3 minutes. keep old cache for 1 day. What is the implementation that we should we use? Is there any setting that we can change beside those default bellow? (I use AFNetworking 3 for request and response). Any help is much appreciate. Thanks
NSURLRequestCachePolicy
NSURLRequest has a cachePolicy property, which specifies the caching behavior of the request according to the following constants:
NSURLRequestUseProtocolCachePolicy: Caching logic defined in the protocol implementation is used for a particular URL load request. This is the default policy.
NSURLRequestReloadIgnoringLocalCacheData: Data should be loaded from the originating source. No existing cache data should be used.
NSURLRequestReloadIgnoringLocalAndRemoteCacheData: Not only should the local cache data be ignored, but proxies and other intermediates should be instructed to disregard their caches so far as the protocol allows.
NSURLRequestReturnCacheDataElseLoad: Existing cached data should be used, regardless of its age or expiration date. If there is no existing data in the cache corresponding to the request, the data is loaded from the originating source.
NSURLRequestReturnCacheDataDontLoad: Existing cache data should be used, regardless of its age or expiration date. If there is no existing data in the cache corresponding to the request, no attempt is made to load the data from the originating source, and the load is considered to have failed, (i.e. “offline” mode).
NSURLRequestReloadRevalidatingCacheData: Existing cache data may be used provided the origin source confirms its validity, otherwise the URL is loaded from the origin source.
You should almost invariably use NSURLRequestUseProtocolCachePolicy unless you're trying to kill caching entirely for some reason.
The cache durations should be set by the server as part of its response (in the Cache-Control header). The iOS URL cache obeys the policies specified in that header.
I've created and application and a paginated api which is hooked up to each other. However i'm a bit confused on what is best practice in terms of only showing updated data. For instance if i retrieve data one day and save it into my mobile database. How will the app the next day know that it should make a request and only show that particular data that just has been fetched from the database. Do i need to make somekind of flag or look at createdAt?
When making the request, include either the If-None-Match header with the local resource's ETag or the If-Modified-Since header with the date the local resource was requested.
Configure your server to look for the header and return a 304 Not Modified if the data hasn't changed. That will at least save you some traffic on the responses.
In addition, if the resource data is relatively static, or if the client can tolerate having stale client data, then you can add caching headers to your response. As long as the cached request is valid, the request will never leave your client.
Ideally, you want do design your API to support this where possible. For example, have the request "give me all things in 50 meters" return a list of URIs. Then the API will only have to hit the server for those URIs which are stale.
We are currently spiking a Varnish implementation to see if it would be appropriate to sit in front of our Rails application.
We want Varnish to cache the results of an API call and only hit the application when the client's ETag doesn't match the one stored in Varnish or the client's modified date is before Varnish's.
So far, I have not seen Varnish take these values into account.
We only get cache hits when the secondary requests are within the max age.
Is this expected behaviour?
It is the expected behavior, Varnish does not currently revalidated cached content.
There is some experimental work to do what you want, which may or may not end up in Varnish 4.0 (in a few months).
In the meantime what you can do is set an artificially short TTL, and set a grace time equivalent to your desired TTL. With that configuration, when a request comes in Varnish will send an IMS request to the backend (as long as the cached entry has an ETag of course, otherwise it will be a plain request).
The side effect is that Varnish will also send the cached entry if the backend is down or returns 500—this may or may not be what you want.
What I have done is set the hash() function to use the etag provided.
This means that the first request for an object (no etag provided from client) will get the object itself.
Any subsequent request, or any request that provides the header for etag, will get either an object or the 304.
I am optimization my web page by implementing caching, so if I want the browser not to take data from cache, then I will append a dynamic number as query value.
eg: google.com?val=823746
But some time, if I want to bring data from cache for the below url, the browser is making a new http request to server, its not taking data from cache. Is that because of the question mark in URL ?
eg: http://google.com?
Please provide some reference document link.
Thanks in advance.
Regards,
Navin
Use appropriate HTTP headers.
Search of pragma: no-cache and Expires
Browsers may not cache URLs that contain a query string (part after ? ) unless the headers indicate the expiry time explicitly.
Cache policy is not same across all browsers. If you don't specify appropriate headers the results may be even more unpredictable.
Since query strings are used with dynamically generated pages, the browser may take that hint and fire a new request even if the query string is same.
For example, a desktop browser may err on side of caution and fire a new request. On the other hand a mobile browser with aggressive cache policy may pull the page from cache.