Move rate limit interceptor logic to module (#1100)
This commit is contained in:
parent
2ffb53ddbe
commit
5ea7a40919
|
@ -0,0 +1,29 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '28.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 27
|
||||
versionCode 1
|
||||
versionName '1.0.0'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
compileOnly 'com.squareup.okhttp3:okhttp:3.10.0'
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="eu.kanade.tachiyomi.lib.ratelimit">
|
||||
</manifest>
|
|
@ -0,0 +1,58 @@
|
|||
package eu.kanade.tachiyomi.lib.ratelimit
|
||||
|
||||
import android.os.SystemClock
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* An OkHttp interceptor that handles rate limiting.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* permits = 5, period = 1, unit = seconds => 5 requests per second
|
||||
* permits = 10, period = 2, unit = minutes => 10 requests per 2 minutes
|
||||
*
|
||||
* @param permits {Int} Number of requests allowed within a period of units.
|
||||
* @param period {Long} The limiting duration. Defaults to 1.
|
||||
* @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
||||
*/
|
||||
class RateLimitInterceptor(
|
||||
private val permits: Int,
|
||||
private val period: Long = 1,
|
||||
private val unit: TimeUnit = TimeUnit.SECONDS) : Interceptor {
|
||||
|
||||
private val requestQueue = ArrayList<Long>(permits)
|
||||
private val rateLimitMillis = unit.toMillis(period)
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
synchronized(requestQueue) {
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
val waitTime = if (requestQueue.size < permits) {
|
||||
0
|
||||
} else {
|
||||
val oldestReq = requestQueue[0]
|
||||
val newestReq = requestQueue[permits - 1]
|
||||
|
||||
if (newestReq - oldestReq > rateLimitMillis) {
|
||||
0
|
||||
} else {
|
||||
oldestReq + rateLimitMillis - now // Remaining time
|
||||
}
|
||||
}
|
||||
|
||||
if (requestQueue.size == permits) {
|
||||
requestQueue.removeAt(0)
|
||||
}
|
||||
if (waitTime > 0) {
|
||||
requestQueue.add(now + waitTime)
|
||||
Thread.sleep(waitTime) // Sleep inside synchronized to pause queued requests
|
||||
} else {
|
||||
requestQueue.add(now)
|
||||
}
|
||||
}
|
||||
|
||||
return chain.proceed(chain.request())
|
||||
}
|
||||
|
||||
}
|
|
@ -7,5 +7,7 @@ new File(rootDir, "src").eachDir { dir ->
|
|||
}
|
||||
include ':duktape-stub'
|
||||
include ':preference-stub'
|
||||
include ':lib-ratelimit'
|
||||
project(':duktape-stub').projectDir = new File("lib/duktape-stub")
|
||||
project(':preference-stub').projectDir = new File("lib/preference-stub")
|
||||
project(':preference-stub').projectDir = new File("lib/preference-stub")
|
||||
project(':lib-ratelimit').projectDir = new File("lib/ratelimit")
|
||||
|
|
|
@ -5,11 +5,12 @@ ext {
|
|||
appName = 'Tachiyomi: MangaDex'
|
||||
pkgNameSuffix = 'all.mangadex'
|
||||
extClass = '.MangadexFactory'
|
||||
extVersionCode = 56
|
||||
extVersionCode = 57
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':lib-ratelimit')
|
||||
compileOnly project(':preference-stub')
|
||||
compileOnly 'com.google.code.gson:gson:2.8.2'
|
||||
compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0'
|
||||
|
|
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.all.mangadex
|
|||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.os.SystemClock
|
||||
import android.support.v7.preference.ListPreference
|
||||
import android.support.v7.preference.PreferenceScreen
|
||||
import com.github.salomonbrys.kotson.forEach
|
||||
|
@ -16,6 +15,7 @@ import com.github.salomonbrys.kotson.string
|
|||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
|
@ -28,7 +28,6 @@ import eu.kanade.tachiyomi.source.model.SManga
|
|||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
@ -57,37 +56,7 @@ open class Mangadex(override val lang: String, private val internalLang: String,
|
|||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
private val requestsPerSecond = 4
|
||||
private val lastRequests = ArrayList<Long>(requestsPerSecond)
|
||||
private val rateLimitInterceptor = Interceptor {
|
||||
synchronized(this) {
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
val waitTime = if (lastRequests.size < requestsPerSecond) {
|
||||
0
|
||||
} else {
|
||||
val oldestReq = lastRequests[0]
|
||||
val newestReq = lastRequests[requestsPerSecond - 1]
|
||||
|
||||
if (newestReq - oldestReq > 1000) {
|
||||
0
|
||||
} else {
|
||||
oldestReq + 1000 - now // Remaining time for the next second
|
||||
}
|
||||
}
|
||||
|
||||
if (lastRequests.size == requestsPerSecond) {
|
||||
lastRequests.removeAt(0)
|
||||
}
|
||||
if (waitTime > 0) {
|
||||
lastRequests.add(now + waitTime)
|
||||
Thread.sleep(waitTime) // Sleep inside synchronized to pause queued requests
|
||||
} else {
|
||||
lastRequests.add(now)
|
||||
}
|
||||
}
|
||||
|
||||
it.proceed(it.request())
|
||||
}
|
||||
private val rateLimitInterceptor = RateLimitInterceptor(4)
|
||||
|
||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||
.addNetworkInterceptor(rateLimitInterceptor)
|
||||
|
|
Loading…
Reference in New Issue