c-an
c-an

Reputation: 4090

Keep getting java.io.InterruptedIOException: timeout in Retrofit2

I get java.io.InterruptedIOException: timeout rarely in my office.

However, it keeps occurring in stores.

So, I set

client.addInterceptor(httpLoggingInterceptor)
            .connectTimeout(5, TimeUnit.SECONDS)
            .callTimeout(5, TimeUnit.SECONDS)
            .readTimeout(5, TimeUnit.SECONDS)
            .writeTimeout(5, TimeUnit.SECONDS)
            .retryOnConnectionFailure(true)

But, this didn't work.

So, What I did in .enqueue()

fun startPay(){
    // ... Service Generator and so on...
    override fun onFailure(call: Call<ResultData>, t: Throwable) {
        when(t){
            is InterruptedIOException ->{
                Thread.sleep(1000)
                startPay()
            }
            else->{
                Toast.makeText(this, t.toString(), Toast.LENGTH_SHORT).show()
            }
        }
    }
}

I am guessing this occurs because more than two clicks in short time or I am calling 3 consecutively after 200 response. ex. A call -> 200 response -> B call -> 200 response -> C call -> 200 response -> UI Update. Now, it catches the exception and retry the api call. I think this has no reason to be interrupted by the same thread or other thread..

This solution is fine in general. But the problem is when I pay something It must call just once.

this InterrupedIOException: timeout(Not SocketTimeout) seems cancel the connection before getting the response from server, the server is sending the response well and has no problem.

Some questions in stackoverflow mentioned Crashlystics. I am using it but I shouldn't remove it.

I am using:

    // Firebase
    // Import the BoM for the Firebase platform
    implementation platform('com.google.firebase:firebase-bom:26.0.0')
    implementation 'com.google.firebase:firebase-core'
    implementation 'com.google.firebase:firebase-auth'
    implementation 'com.google.firebase:firebase-auth-ktx'
    implementation 'com.google.firebase:firebase-config' // remote config
    implementation 'com.google.firebase:firebase-analytics'
    implementation 'com.google.firebase:firebase-analytics-ktx'
    implementation 'com.google.firebase:firebase-crashlytics'
    implementation 'com.google.firebase:firebase-perf'
    implementation 'com.google.firebase:firebase-config-ktx'

    // Network
    // okhttp: https://github.com/square/okhttp
    implementation 'com.squareup.okhttp3:okhttp:4.7.2'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.7.2'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
    implementation "com.squareup.retrofit2:converter-gson:2.9.0"
    implementation 'org.conscrypt:conscrypt-android:2.2.1' // optional

The error is here:

java.io.InterruptedIOException: timeout
W/System.err: java.io.InterruptedIOException: timeout
W/System.err:     at okhttp3.internal.connection.RealCall.timeoutExit(RealCall.kt:384)
W/System.err:     at okhttp3.internal.connection.RealCall.maybeReleaseConnection(RealCall.kt:346)
W/System.err:     at okhttp3.internal.connection.RealCall.noMoreExchanges$okhttp(RealCall.kt:310)
W/System.err:     at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:205)
W/System.err:     at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:502)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
W/System.err:     at java.lang.Thread.run(Thread.java:761)
W/System.err: Caused by: java.io.IOException: Canceled
W/System.err:     at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:72)
W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
W/System.err:     at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.kt:219)
W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
W/System.err:     at com.example.helper.api.ServiceGenerator$createService$$inlined$-addInterceptor$1.intercept(Interceptor.kt:87)
W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
W/System.err:     at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:197)
W/System.err:   ... 4 more

Upvotes: 5

Views: 5785

Answers (2)

Oleg Sokolov
Oleg Sokolov

Reputation: 1143

If you still need callTimeout, you can add a custom Interceptor:

class InterruptedIOExceptionInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        return try {
            chain.proceed(chain.request())
        } catch (e: InterruptedIOException) {
            Response.Builder()
                .code(408)
                .message("Request Timeout")
                .protocol(Protocol.HTTP_1_1) //fixme hardcoded protocol
                .request(chain.request())
                .body("Request Timeout".toResponseBody("text/html".toMediaTypeOrNull()))
                .build()
        }
    }
}

add it to the OkHttpClient.Builder:

okHttpClient.addInterceptor(InterruptedIOExceptionInterceptor())

Upvotes: 0

BArtWell
BArtWell

Reputation: 4044

One of the possible reasons is a bug in okhttp3. When you settled timeouts, POST requests can become canceled under IPv6 networks with a log like described. I am able to reproduce it using IPv6Droid application for tests (it is not free).

To fix it just remove .callTimeout().

Upvotes: 1

Related Questions