Add support to kotlin.time APIs in the rate limit interceptor (#9797)
* Add support to kotlin.time APIs in the rate limit interceptor. * Add a missing line break in the doc. * Move the specific host to the same file. * Add kotlin.time rule to Proguard and remove specific host rule. * Mark the old version as deprecated and address review. * Remove unused import. * Remove yet another unused import.
This commit is contained in:
parent
7798186c32
commit
9b6567f5e4
4 changed files with 87 additions and 14 deletions
1
app/proguard-rules.pro
vendored
1
app/proguard-rules.pro
vendored
|
@ -8,6 +8,7 @@
|
||||||
-keep,allowoptimization class kotlin.** { public protected *; }
|
-keep,allowoptimization class kotlin.** { public protected *; }
|
||||||
-keep,allowoptimization class kotlinx.coroutines.** { public protected *; }
|
-keep,allowoptimization class kotlinx.coroutines.** { public protected *; }
|
||||||
-keep,allowoptimization class kotlinx.serialization.** { public protected *; }
|
-keep,allowoptimization class kotlinx.serialization.** { public protected *; }
|
||||||
|
-keep,allowoptimization class kotlin.time.** { public protected *; }
|
||||||
-keep,allowoptimization class okhttp3.** { public protected *; }
|
-keep,allowoptimization class okhttp3.** { public protected *; }
|
||||||
-keep,allowoptimization class okio.** { public protected *; }
|
-keep,allowoptimization class okio.** { public protected *; }
|
||||||
-keep,allowoptimization class rx.** { public protected *; }
|
-keep,allowoptimization class rx.** { public protected *; }
|
||||||
|
|
|
@ -27,7 +27,7 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.concurrent.TimeUnit
|
import kotlin.time.Duration.Companion.minutes
|
||||||
|
|
||||||
class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||||
|
|
||||||
private val authClient = client.newBuilder()
|
private val authClient = client.newBuilder()
|
||||||
.addInterceptor(interceptor)
|
.addInterceptor(interceptor)
|
||||||
.rateLimit(permits = 85, period = 1, unit = TimeUnit.MINUTES)
|
.rateLimit(permits = 85, period = 1.minutes)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
suspend fun addLibManga(track: Track): Track {
|
suspend fun addLibManga(track: Track): Track {
|
||||||
|
|
|
@ -8,10 +8,17 @@ import java.io.IOException
|
||||||
import java.util.ArrayDeque
|
import java.util.ArrayDeque
|
||||||
import java.util.concurrent.Semaphore
|
import java.util.concurrent.Semaphore
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
import kotlin.time.toDuration
|
||||||
|
import kotlin.time.toDurationUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An OkHttp interceptor that handles rate limiting.
|
* An OkHttp interceptor that handles rate limiting.
|
||||||
*
|
*
|
||||||
|
* This uses `java.time` APIs and is the legacy method, kept
|
||||||
|
* for compatibility reasons with existing extensions.
|
||||||
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
*
|
*
|
||||||
* permits = 5, period = 1, unit = seconds => 5 requests per second
|
* permits = 5, period = 1, unit = seconds => 5 requests per second
|
||||||
|
@ -19,27 +26,43 @@ import java.util.concurrent.TimeUnit
|
||||||
*
|
*
|
||||||
* @since extension-lib 1.3
|
* @since extension-lib 1.3
|
||||||
*
|
*
|
||||||
* @param permits {Int} Number of requests allowed within a period of units.
|
* @param permits [Int] Number of requests allowed within a period of units.
|
||||||
* @param period {Long} The limiting duration. Defaults to 1.
|
* @param period [Long] The limiting duration. Defaults to 1.
|
||||||
* @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
* @param unit [TimeUnit] The unit of time for the period. Defaults to seconds.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("Use the version with kotlin.time APIs instead.")
|
||||||
fun OkHttpClient.Builder.rateLimit(
|
fun OkHttpClient.Builder.rateLimit(
|
||||||
permits: Int,
|
permits: Int,
|
||||||
period: Long = 1,
|
period: Long = 1,
|
||||||
unit: TimeUnit = TimeUnit.SECONDS,
|
unit: TimeUnit = TimeUnit.SECONDS,
|
||||||
) = addInterceptor(RateLimitInterceptor(null, permits, period, unit))
|
) = addInterceptor(RateLimitInterceptor(null, permits, period.toDuration(unit.toDurationUnit())))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An OkHttp interceptor that handles rate limiting.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
*
|
||||||
|
* permits = 5, period = 1.seconds => 5 requests per second
|
||||||
|
* permits = 10, period = 2.minutes => 10 requests per 2 minutes
|
||||||
|
*
|
||||||
|
* @since extension-lib 1.5
|
||||||
|
*
|
||||||
|
* @param permits [Int] Number of requests allowed within a period of units.
|
||||||
|
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
||||||
|
*/
|
||||||
|
fun OkHttpClient.Builder.rateLimit(permits: Int, period: Duration = 1.seconds) =
|
||||||
|
addInterceptor(RateLimitInterceptor(null, permits, period))
|
||||||
|
|
||||||
/** We can probably accept domains or wildcards by comparing with [endsWith], etc. */
|
/** We can probably accept domains or wildcards by comparing with [endsWith], etc. */
|
||||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||||
internal class RateLimitInterceptor(
|
internal class RateLimitInterceptor(
|
||||||
private val host: String?,
|
private val host: String?,
|
||||||
private val permits: Int,
|
private val permits: Int,
|
||||||
period: Long,
|
period: Duration,
|
||||||
unit: TimeUnit,
|
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
|
|
||||||
private val requestQueue = ArrayDeque<Long>(permits)
|
private val requestQueue = ArrayDeque<Long>(permits)
|
||||||
private val rateLimitMillis = unit.toMillis(period)
|
private val rateLimitMillis = period.inWholeMilliseconds
|
||||||
private val fairLock = Semaphore(1, true)
|
private val fairLock = Semaphore(1, true)
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
package eu.kanade.tachiyomi.network.interceptor
|
package eu.kanade.tachiyomi.network.interceptor
|
||||||
|
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
import kotlin.time.toDuration
|
||||||
|
import kotlin.time.toDurationUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An OkHttp interceptor that handles given url host's rate limiting.
|
* An OkHttp interceptor that handles given url host's rate limiting.
|
||||||
*
|
*
|
||||||
|
* This uses Java Time APIs and is the legacy method, kept
|
||||||
|
* for compatibility reasons with existing extensions.
|
||||||
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
*
|
*
|
||||||
* httpUrl = "api.manga.com".toHttpUrlOrNull(), permits = 5, period = 1, unit = seconds => 5 requests per second to api.manga.com
|
* httpUrl = "api.manga.com".toHttpUrlOrNull(), permits = 5, period = 1, unit = seconds => 5 requests per second to api.manga.com
|
||||||
|
@ -14,14 +22,55 @@ import java.util.concurrent.TimeUnit
|
||||||
*
|
*
|
||||||
* @since extension-lib 1.3
|
* @since extension-lib 1.3
|
||||||
*
|
*
|
||||||
* @param httpUrl {HttpUrl} The url host that this interceptor should handle. Will get url's host by using HttpUrl.host()
|
* @param httpUrl [HttpUrl] The url host that this interceptor should handle. Will get url's host by using HttpUrl.host()
|
||||||
* @param permits {Int} Number of requests allowed within a period of units.
|
* @param permits [Int] Number of requests allowed within a period of units.
|
||||||
* @param period {Long} The limiting duration. Defaults to 1.
|
* @param period [Long] The limiting duration. Defaults to 1.
|
||||||
* @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
* @param unit [TimeUnit] The unit of time for the period. Defaults to seconds.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("Use the version with kotlin.time APIs instead.")
|
||||||
fun OkHttpClient.Builder.rateLimitHost(
|
fun OkHttpClient.Builder.rateLimitHost(
|
||||||
httpUrl: HttpUrl,
|
httpUrl: HttpUrl,
|
||||||
permits: Int,
|
permits: Int,
|
||||||
period: Long = 1,
|
period: Long = 1,
|
||||||
unit: TimeUnit = TimeUnit.SECONDS,
|
unit: TimeUnit = TimeUnit.SECONDS,
|
||||||
) = addInterceptor(RateLimitInterceptor(httpUrl.host, permits, period, unit))
|
) = addInterceptor(RateLimitInterceptor(httpUrl.host, permits, period.toDuration(unit.toDurationUnit())))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An OkHttp interceptor that handles given url host's rate limiting.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
*
|
||||||
|
* httpUrl = "https://api.manga.com".toHttpUrlOrNull(), permits = 5, period = 1.seconds => 5 requests per second to api.manga.com
|
||||||
|
* httpUrl = "https://imagecdn.manga.com".toHttpUrlOrNull(), permits = 10, period = 2.minutes => 10 requests per 2 minutes to imagecdn.manga.com
|
||||||
|
*
|
||||||
|
* @since extension-lib 1.5
|
||||||
|
*
|
||||||
|
* @param httpUrl [HttpUrl] The url host that this interceptor should handle. Will get url's host by using HttpUrl.host()
|
||||||
|
* @param permits [Int] Number of requests allowed within a period of units.
|
||||||
|
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
||||||
|
*/
|
||||||
|
fun OkHttpClient.Builder.rateLimitHost(
|
||||||
|
httpUrl: HttpUrl,
|
||||||
|
permits: Int,
|
||||||
|
period: Duration = 1.seconds,
|
||||||
|
) = addInterceptor(RateLimitInterceptor(httpUrl.host, permits, period))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An OkHttp interceptor that handles given url host's rate limiting.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
*
|
||||||
|
* url = "https://api.manga.com", permits = 5, period = 1.seconds => 5 requests per second to api.manga.com
|
||||||
|
* url = "https://imagecdn.manga.com", permits = 10, period = 2.minutes => 10 requests per 2 minutes to imagecdn.manga.com
|
||||||
|
*
|
||||||
|
* @since extension-lib 1.5
|
||||||
|
*
|
||||||
|
* @param url [String] The url host that this interceptor should handle. Will get url's host by using HttpUrl.host()
|
||||||
|
* @param permits [Int] Number of requests allowed within a period of units.
|
||||||
|
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
||||||
|
*/
|
||||||
|
fun OkHttpClient.Builder.rateLimitHost(
|
||||||
|
url: String,
|
||||||
|
permits: Int,
|
||||||
|
period: Duration = 1.seconds,
|
||||||
|
) = addInterceptor(RateLimitInterceptor(url.toHttpUrlOrNull()?.host, permits, period))
|
||||||
|
|
Reference in a new issue