Retrofitting + OkHTTP - response cache not working

I know there have been many similar questions, but I read them all and none of them helped.

So here's my problem:

I am using retrofit + okhttp to get some data from the API and I would like to cache it. Unfortunately, I do not have admin access to the API server, so I cannot change the headers returned by the server. (currently the server is returning Cache-control: private)

So I decided to use okhttp header spoofing to insert the appropriate cache headers. Unfortunately, no matter what I do, the caching doesn't seem to work.

I am initializing the api service like this:

int cacheSize = 10 * 1024 * 1024; // 10 MiB
File cacheFile = new File(context.getCacheDir(), "thumbs");
final Cache cache = new Cache(cacheFile, cacheSize);

OkHttpClient client = new OkHttpClient();
client.setCache(cache);
client.interceptors().add(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response originalResponse = chain.proceed(chain.request());
        return originalResponse.newBuilder()
                .removeHeader("Access-Control-Allow-Origin")
                .removeHeader("Vary")
                .removeHeader("Age")
                .removeHeader("Via")
                .removeHeader("C3-Request")
                .removeHeader("C3-Domain")
                .removeHeader("C3-Date")
                .removeHeader("C3-Hostname")
                .removeHeader("C3-Cache-Control")
                .removeHeader("X-Varnish-back")
                .removeHeader("X-Varnish")
                .removeHeader("X-Cache")
                .removeHeader("X-Cache-Hit")
                .removeHeader("X-Varnish-front")
                .removeHeader("Connection")
                .removeHeader("Accept-Ranges")
                .removeHeader("Transfer-Encoding")
                .header("Cache-Control", "public, max-age=60")
              //.header("Expires", "Mon, 27 Apr 2015 08:15:14 GMT")
                .build();
    }
});

RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint(API_ROOT)
    .setLogLevel(RestAdapter.LogLevel.HEADERS_AND_ARGS)
    .setClient(new OkClient(client))
    .setConverter(new SimpleXMLConverter(false))
    .setRequestInterceptor(new RequestInterceptor() {
        @Override
        public void intercept(RequestFacade request) {
            if (Network.isConnected(context)) {
                int maxAge = 60; // read from cache for 2 minutes
                request.addHeader("Cache-Control", "public, max-age=" + maxAge);
            } else {
                int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
                request.addHeader("Cache-Control",
                    "public, only-if-cached, max-stale=" + maxStale);
            }
        }
    })
    .build();
api = restAdapter.create(ApiService.class);

      

Of course, there is no need to remove all of these headers, but I wanted to keep the answer as clean as possible to eliminate some of the clutter from these additional headers.

As you can see, I tried to fool the Expires and Date header as well (I tried to remove them by setting them so that there is exactly the max-age difference between them, and also setting Expires far into the future). I've also experimented with different Cache-control values ​​but no luck.

I made sure that cacheFile exists, isDirectory and is writable by the application.

These are the request and response headers that are registered directly by modification:

Request:
Cache-Control: public, max-age=60
---> END HTTP (no body)

Response:
Date: Mon, 27 Apr 2015 08:41:10 GMT
Server: Apache/2.2.22 (Ubuntu)
Expires: Mon, 27 Apr 2015 08:46:10 GMT
Content-Type: text/xml; charset=UTF-8
OkHttp-Selected-Protocol: http/1.1
OkHttp-Sent-Millis: 1430124070000
OkHttp-Received-Millis: 1430124070040
Cache-Control: public, max-age=60
<--- END HTTP (-1-byte body)
<--- BODY: ...

      

And finally, one strange incident: at some point the cache worked for several minutes. I was getting reasonable hit counts, even offline queries were returning cached values. (This happened when using the fine tuning posted here). But when I restarted the application everything returned to "normal" (constant 0 hits).

Co, if anyone knows what might be the problem here, I would really appreciate any help :)

+3


source to share


2 answers


Use networkInterceptors () instead of interceptors (). This, combined with your strategy of removing any headers that are somewhat cache-related, will work. This is the short answer.

When you use interceptors to change headers, it does not make any adjustments until CacheStrategy.isCacheable () is called. It's worth taking a look at the CacheStrategy and CacheControl classes to see how OKHttp handles cache-related headers. It's also helpful to do ctrl + f "cache" at http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html



I'm not sure if the documentation of networkInterceptors () and interceptors () is unclear or if there is a bug. As soon as I look into this, I'll update this answer.

+8


source


One more thing to add here besides Brendan Weinstein's answer to confirm that the OkHttp3 cache won't work with post requests.



+7


source







All Articles