Custom timeouts with retrofit

I recently ran into the problem of customising timeouts for different API calls when using retrofit.

Ideally, you would be able to specify a timeout using a custom annotation.

interface Api {
    @Timeout(60000)
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user: String): Call<List<Repo>>
}

However, without creating a wrapper around retrofit this isn’t possible.

Instead I created a custom interceptor that uses the @Headers annotation to parse timeouts. (This will override the default timeouts you have set on your OkHttpClient)

class TimeoutInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()

        val timeout = request.header("X-Timeout")?.toInt() ?: 0

        return if (timeout > 0) {
            chain.withWriteTimeout(timeout, TimeUnit.MILLISECONDS)
                    .withReadTimeout(timeout, TimeUnit.MILLISECONDS)
                    .withConnectTimeout(timeout, TimeUnit.MILLISECONDS)
                    .proceed(request.newBuilder().removeHeader("X-Timeout").build())
        } else {
            chain.proceed(request)
        }
    }
}

To use this you add a header with your timeout to the API declaration. The interceptor will consume this and apply the custom timeout to the chain.

@Headers("X-Timeout: 60000")
@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String): Call<List<Repo>>

Don’t forget to add your interceptor to your OkHttpClient

val client = OkHttpClient.Builder()
        .addInterceptor(TimeoutInterceptor())
        .build()

val retrofit = Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .client(client)
        .build()

val service = retrofit.create(GitHubService::class.java)

This functionality was added in OkHttp 3.9.0 so ensure you are targeting the latest version.