Bui Quang Huy
Bui Quang Huy

Reputation: 1794

Put all ongoing requests to queue retrofit and okhttp

I'm working with the authenticator of OKHttp that will retry to get new access token if we got 401 status error, but my app have to call many APIs in the same time, resulting in corrupted data, because existed refresh token will be removed when request - but the other API caller still depend on this token to use. So my question : is there anyway to put the request in queue (or at least cancel) all other api request when we got 401 error status code?

This is my authenticator:

 public Request authenticate(Route route, Response response) throws IOException {
       // Refresh your access_token using a synchronous api request
          access_token = getNewAccessTokenHere();
                // Add new header to rejected request and retry it
                return response.request().newBuilder()
                        .header("Authorization", "Bearer " + access_token)
                        .build();
            } else {
                ToastUtil.toast("login again");
                return null;
            }

    }

My goal is let other api waiting for the response of first request and use the new access_token.

Upvotes: 5

Views: 5847

Answers (2)

fredrib
fredrib

Reputation: 61

I know this question is quite old, but I found myself having the same problem lately and I came by a solution which has worked for me and I believe it could help someone else in the same situation.

The solution to attach a Dispatcher to the OkHttp client and limit the amount of max requests does not seem to work on retrofit out of the box, as stated by Jake Wharthon .

So my solution was to synchronize the authenticate method on my custom Authenticator, making concurrent calls, to my singleton Retrofit instance, wait until the authenticate routine is finished for each thread. This way, the first call with an unauthorized response can refresh the token and inform to the next calls, which also got an unauthorized response, that a new access token is already available.

public class MyAuthenticator implements Authenticator {

    private boolean isRefreshed = false;


    // Call when a new request is being made. Concurrent request should call this method to enable the refresh routine
    public void newRequest(){
        isRefreshed = false;
    }

    @Nullable
    @Override
    public synchronized Request authenticate(@NonNull Route route, @NonNull Response response) throws IOException { // Synchronize the method to avoid refreshing thread overlapping
        if (responseCount(response) > 3) {
            return null;
        }

        if (!isRefreshed){
            // Refresh your access_token using a synchronous api request
            String accessToken = getNewAccessTokenHere();

            // Saves the new access token
            saveNewAccessToken(accessToken);
            isRefreshed = true;

            // Add new header to rejected request and retry it
            return response.request().newBuilder()
                .removeHeader("Authorization") // removes the old header, avoiding duplications
                .addHeader("Authorization", "Bearer " + accessToken)
                .build();

        }
        else{ // Access token already refreshed so retry with the new one

            // Get the saved access token
            String accessToken = getAccessToken(); 

            return response.request()
                        .newBuilder()
                        .removeHeader("Authorization")
                        .addHeader("Authorization", accessToken)
                        .build();
    }

}

private int responseCount(Response response) {
    int result = 1;
    while ((response = response.priorResponse()) != null) {
        result++;
    }
    return result;
}

}

Upvotes: 4

Jesse Wilson
Jesse Wilson

Reputation: 40593

You can use the Dispatcher to access all in-flight calls and cancel them.

https://square.github.io/okhttp/3.x/okhttp/okhttp3/Dispatcher.html

Upvotes: 2

Related Questions