<zh-dmzj>Add ratelimit to fix HTTP 429 and return webpage url to "Open in browser" and "Share manga". (#5537)

* <zh-dmzj>Add ratelimit to fix HTTP 429 and return human readable webpage url to "Open in browser" and "Share manga".

* Add ratelimit interceptor that only handle specific url host.
This commit is contained in:
Oldwangtouchtouchdoge
2021-01-25 20:29:10 +08:00
committed by GitHub
parent e1bffd90ab
commit b77d42a941
3 changed files with 226 additions and 10 deletions

View File

@ -0,0 +1,65 @@
package eu.kanade.tachiyomi.lib.ratelimit
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 = Httpurl.parse("api.manga.com"), permits = 5, period = 1, unit = seconds => 5 requests per second to api.manga.com
* httpUrl = Httpurl.parse("imagecdn.manga.com"), 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())
}
}