Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ProtocolException: Too many follow-up requests: 21 with Proguard and OkHttp 3.0 in Android

I am creating an app using Retrofit 2.1.0 and OkHttp 3.4.2.

In debug mode with minifyEnabled set to false everything works perfectly but as soon as i change minifyEnabled to true i get the following exception:

HTTP FAILED: java.net.ProtocolException: Too many follow-up requests: 21

My Proguard rules for OkHttp are as follows:

-keep class com.squareup.okhttp3.** {
    *;
}
-dontwarn okhttp3.**
-dontwarn okio.**

I can't understand why this exception is thrown and i do not understand why the app is seems to be making 21 follow up requests. Can anyone help me?

like image 883
Christian Leicht Jørgensen Avatar asked Nov 15 '16 08:11

Christian Leicht Jørgensen


People also ask

What does too many follow up requests 21 mean?

Typically this happens when you get into a redirect loop, or when you authenticate with the wrong username, and then retry with the same wrong username. You can fix your Authenticator to check if it is a follow-up request and not retry in that case.

What is OkHttp in Android?

OkHttp is an efficient HTTP & HTTP/2 client for Android and Java applications. It comes with advanced features, such as connection pooling (if HTTP/2 isn't available), transparent GZIP compression, and response caching, to avoid the network completely for repeated requests.


1 Answers

I've just had this exact same error: java.net.ProtocolException: Too many follow-up requests: 21. For your interest I'm using Retrofit 2.5.0 and OkHttp 3.14.1, although versions don't matter much.

I too only have this error with Proguard enabled (actually I'm using R8 but the result is the same). This is important and hints the root cause.

What's the problem? I authenticate using OAuth, adding an "Authorization" header as usual. When the token expires the server sends a 401 Unauthorized. Since I'm using an OkHttp Authenticator to refresh the token, when a 401 is received the authenticate method is called.

The problem is that I parse the response of this 401 Unauthorized request using Gson, like this:

override fun authenticate(route: Route?, response: Response): Request? {
    val responseError: ResponseError? = response.body()?.let {
        Gson().fromJson(it.string(), ResponseError::class.java) // <- Fails!
    }
    // Check server response and decide if should refresh token...
}

But since the class ResponseError is obfuscated by proguard it's fields don't match the names of the JSON that the server sends, which makes Gson().fromJson fail, and consequently the token is not refreshed. The result is that a network call is performed repeatedly until the Exception is thrown.

The fix is trivial. Just add @Keep to ResponseError:

import androidx.annotation.Keep

@Keep
data class ResponseError(
    val error: String? = null,
    val error_description: String? = null
)

Since your problem happens with proguard enabled only, it's likely that a request or response class is being obfuscated, which makes the JSON parsing fail. Or you may be saving some JSON to Shared Preferences using Gson.fromJson and Gson.fromJson with an obfuscated class.

To fix the problem add @SerializedName and/or @Keep to all your requests and responses (specially enums, which give more trouble). Alternatively you can simply put the all the requests and responses into a single package and exclude it. In addition to requests and responses, pay special attention to any Gson.fromJson and Gson.fromJson call too.

To help diagnose the problem you can check what proguard/R8 does by checking the mappings file you'll find in app/build/outputs/mapping/release/mapping.txt (for a release build). It contains all the transformations that proguard/R8 has done to your code.

You can also analyze the APK by doing Build -> Analyze APK... You'll see which classes are being obfuscated in a very simple way. You can analyze 2 APKs at the same time (one minified and one not minified) and compare them.

like image 81
Albert Vila Calvo Avatar answered Nov 16 '22 01:11

Albert Vila Calvo