From 3bea94bf1f926a17737cff733435365ab43cd61c Mon Sep 17 00:00:00 2001 From: Koitharu Date: Fri, 11 Aug 2023 11:21:25 +0300 Subject: [PATCH] Handle 429 TooManyRequests error --- .../exceptions/TooManyRequestExceptions.kt | 9 +++++ .../kotatsu/core/network/CommonHeaders.kt | 1 + .../kotatsu/core/network/NetworkModule.kt | 1 + .../core/network/RateLimitInterceptor.kt | 36 +++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/TooManyRequestExceptions.kt create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/core/network/RateLimitInterceptor.kt diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/TooManyRequestExceptions.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/TooManyRequestExceptions.kt new file mode 100644 index 000000000..a2908547d --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/TooManyRequestExceptions.kt @@ -0,0 +1,9 @@ +package org.koitharu.kotatsu.core.exceptions + +import okio.IOException +import java.util.Date + +class TooManyRequestExceptions( + val url: String, + val retryAt: Date?, +) : IOException() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt index 1454542ea..9a31233a0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt @@ -15,6 +15,7 @@ object CommonHeaders { const val AUTHORIZATION = "Authorization" const val CACHE_CONTROL = "Cache-Control" const val PROXY_AUTHORIZATION = "Proxy-Authorization" + const val RETRY_AFTER = "Retry-After" val CACHE_CONTROL_NO_STORE: CacheControl get() = CacheControl.Builder().noStore().build() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/NetworkModule.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/NetworkModule.kt index 1c91966a0..e9e6a6dc6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/NetworkModule.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/NetworkModule.kt @@ -67,6 +67,7 @@ interface NetworkModule { cache(cache) addInterceptor(GZipInterceptor()) addInterceptor(CloudFlareInterceptor()) + addInterceptor(RateLimitInterceptor()) if (BuildConfig.DEBUG) { addInterceptor(CurlLoggingInterceptor()) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/RateLimitInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/RateLimitInterceptor.kt new file mode 100644 index 000000000..83e3dbb6e --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/RateLimitInterceptor.kt @@ -0,0 +1,36 @@ +package org.koitharu.kotatsu.core.network + +import okhttp3.Interceptor +import okhttp3.Response +import okhttp3.internal.closeQuietly +import org.koitharu.kotatsu.core.exceptions.TooManyRequestExceptions +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale +import java.util.concurrent.TimeUnit + +class RateLimitInterceptor : Interceptor { + + private val dateFormat = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ZZZ", Locale.ENGLISH) + + override fun intercept(chain: Interceptor.Chain): Response { + val response = chain.proceed(chain.request()) + if (response.code == 429) { + val retryDate = response.header(CommonHeaders.RETRY_AFTER)?.parseRetryDate() + val request = response.request + response.closeQuietly() + throw TooManyRequestExceptions( + url = request.url.toString(), + retryAt = retryDate, + ) + } + return response + } + + private fun String.parseRetryDate(): Date? { + toIntOrNull()?.let { + return Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(it.toLong())) + } + return dateFormat.parse(this) + } +}