Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTTP 504 Unsatisfiable Request (only-if-cached)

Retrofit2.2.0 and okhttp3.9.1 offline caching not working, When I request offline data will throw an exception that is HTTP 504 Unsatisfiable Request (only-if-cached). Data are loading in the device from the internet, but offline mode cannot retrieval it.

Demonstration: enter image description here

The following code describes the API interface.

public class TestApi {


    private static TestService testService;

    private static final String TEST_URL = "https://httpbin.org/";


    public static TestService getTestService() {

        if(testService == null) {
            synchronized (TestApi.class) {
                if(testService == null) {
                    testService = XApi.getInstance().getRetrofit(TEST_URL, true).create(TestService.class);
                }
            }
        }
        return testService;
    }

}

The following code describes the API Service.

public interface TestService {

    @GET("/cache/60")
    Flowable<TestBean> getTestDate();

}

The following code describes the cache control interceptor.

public class CachingControlInterceptor {

    private static final int TIMEOUT_CONNECT = 60; //60 second
    private static final int TIMEOUT_DISCONNECT = 60 * 60 * 24 * 7; //7天

    public static final Interceptor REWRITE_RESPONSE_INTERCEPTOR = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            String cache = chain.request().header("cache");
            okhttp3.Response originalResponse = chain.proceed(chain.request());
            String cacheControl = originalResponse.header("Cache-Control");
            if (cacheControl == null) {
                if (cache == null || "".equals(cache)) {
                    cache = TIMEOUT_CONNECT + "";
                }
                originalResponse = originalResponse.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public, max-age=" + cache)
                    .build();
                return originalResponse;
            } else {
                return originalResponse;
            }
        }
    };

    public static final Interceptor REWRITE_RESPONSE_INTERCEPTOR_OFFLINE = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            if (!NetworkUtils.isConnected()) {
                request = request.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public, only-if-cached, max-stale="+TIMEOUT_DISCONNECT)
                    .build();
            }
            return chain.proceed(request);
        }
    };

}

Finally, add an interceptor to OkHttpClient.Builder

Retrofit.Builder builder = new Retrofit.Builder()
    .baseUrl(baseUrl)
    .client(getClient(baseUrl, provider))
    .addConverterFactory(GsonConverterFactory.create());

builder.addNetworkInterceptor(CachingControlInterceptor.REWRITE_RESPONSE_INTERCEPTOR);
builder.addInterceptor(CachingControlInterceptor.REWRITE_RESPONSE_INTERCEPTOR_OFFLINE);

And I don't know how to solve it.

Hope anyone could help me!

like image 681
Yanqilong Avatar asked Jan 02 '18 08:01

Yanqilong


2 Answers

I still don't really understand what exactly you are trying to do, but starting with a simpler executable example, what is wrong with the following?

package com.baulsupp.oksocial;

import okhttp3.*;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.scalars.ScalarsConverterFactory;
import retrofit2.http.GET;

import java.io.File;
import java.io.IOException;

public class TestRequest {
    private static boolean connected = true;

    public interface TestService {
        @GET("/cache/60")
        Call<String> getTestDate();
    }

    public static final Interceptor REWRITE_RESPONSE_INTERCEPTOR_OFFLINE = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            if (isConnected()) {
                request = request.newBuilder().cacheControl(CacheControl.FORCE_NETWORK).build();
            } else {
                request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
            }
            Response response = chain.proceed(request);

            System.out.println("network: " + response.networkResponse());
            System.out.println("cache: " + response.cacheResponse());

            return response;
        }
    };

    private static boolean isConnected() {
        return connected;
    }

    public static void main(String[] args) throws IOException {

        OkHttpClient.Builder clientBuilder =
                new OkHttpClient.Builder().cache(new Cache(new File("/tmp/http"), 10 * 1024 * 1024));

        clientBuilder.addInterceptor(REWRITE_RESPONSE_INTERCEPTOR_OFFLINE);

        Retrofit builder = new Retrofit.Builder()
                .addConverterFactory(ScalarsConverterFactory.create())
                .baseUrl("https://httpbin.org/")
                .client(clientBuilder.build())
                .build();

        TestService service = builder.create(TestService.class);

        connected = true;

        String online = service.getTestDate().execute().body();
        System.out.println(online);

        connected = false;

        String offline = service.getTestDate().execute().body();
        System.out.println(offline);
    }
}

Outputs

network: Response{protocol=http/1.1, code=200, message=OK, url=https://httpbin.org/cache/60}
cache: null
{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Cache-Control": "no-cache", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "okhttp/3.9.1"
  }, 
  "origin": "82.5.95.16", 
  "url": "https://httpbin.org/cache/60"
}

network: null
cache: Response{protocol=http/1.1, code=200, message=OK, url=https://httpbin.org/cache/60}
{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Cache-Control": "no-cache", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "okhttp/3.9.1"
  }, 
  "origin": "82.5.95.16", 
  "url": "https://httpbin.org/cache/60"
}
like image 171
Yuri Schimke Avatar answered Sep 29 '22 06:09

Yuri Schimke


only-if-cached: Set by the client to indicate "do not use the network" for the response. The cache should either respond using a stored response, or respond with a 504 status code. Conditional headers such as If-None-Match should not be set. There is no effect if only-if-cached is set by a server as part of a response. (refer:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Cache_request_directives)

Therefore, "only-if-cached" is a cache request directive and should not be used in the response header. But you can try adding ("Cache-Control", "max-stale, only-if-cached") to the Request header to permit stale cached-only responses.

like image 25
Ken Wu Avatar answered Sep 29 '22 06:09

Ken Wu