|
@@ -0,0 +1,65 @@
|
|
|
+package eu.kanade.tachiyomi.network
|
|
|
+
|
|
|
+import android.os.SystemClock
|
|
|
+import okhttp3.HttpUrl
|
|
|
+import okhttp3.Interceptor
|
|
|
+import okhttp3.Response
|
|
|
+import java.util.concurrent.TimeUnit
|
|
|
+
|
|
|
+/**
|
|
|
+ * An OkHttp interceptor that handles given url host's rate limiting.
|
|
|
+ *
|
|
|
+ * Examples:
|
|
|
+ *
|
|
|
+ * httpUrl = "api.manga.com".toHttpUrlOrNull(), permits = 5, period = 1, unit = seconds => 5 requests per second to api.manga.com
|
|
|
+ * httpUrl = "imagecdn.manga.com".toHttpUrlOrNull(), permits = 10, period = 2, unit = minutes => 10 requests per 2 minutes to imagecdn.manga.com
|
|
|
+ *
|
|
|
+ * @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 {Long} The limiting duration. Defaults to 1.
|
|
|
+ * @param unit {TimeUnit} The unit of time for the period. Defaults to seconds.
|
|
|
+ */
|
|
|
+class SpecificHostRateLimitInterceptor(
|
|
|
+ private val httpUrl: HttpUrl,
|
|
|
+ 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)
|
|
|
+ private val host = httpUrl.host
|
|
|
+
|
|
|
+ override fun intercept(chain: Interceptor.Chain): Response {
|
|
|
+ if (chain.request().url.host != host) {
|
|
|
+ return chain.proceed(chain.request())
|
|
|
+ }
|
|
|
+ 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())
|
|
|
+ }
|
|
|
+}
|