|
@@ -15,8 +15,8 @@ import io.legado.app.constant.AppPattern.dataUriRegex
|
|
|
import io.legado.app.data.entities.BaseSource
|
|
import io.legado.app.data.entities.BaseSource
|
|
|
import io.legado.app.data.entities.Book
|
|
import io.legado.app.data.entities.Book
|
|
|
import io.legado.app.data.entities.BookChapter
|
|
import io.legado.app.data.entities.BookChapter
|
|
|
-import io.legado.app.exception.ConcurrentException
|
|
|
|
|
import io.legado.app.help.CacheManager
|
|
import io.legado.app.help.CacheManager
|
|
|
|
|
+import io.legado.app.help.ConcurrentRateLimiter
|
|
|
import io.legado.app.help.JsExtensions
|
|
import io.legado.app.help.JsExtensions
|
|
|
import io.legado.app.help.config.AppConfig
|
|
import io.legado.app.help.config.AppConfig
|
|
|
import io.legado.app.help.exoplayer.ExoPlayerHelper
|
|
import io.legado.app.help.exoplayer.ExoPlayerHelper
|
|
@@ -46,7 +46,6 @@ import io.legado.app.utils.isJsonArray
|
|
|
import io.legado.app.utils.isJsonObject
|
|
import io.legado.app.utils.isJsonObject
|
|
|
import io.legado.app.utils.isXml
|
|
import io.legado.app.utils.isXml
|
|
|
import io.legado.app.utils.splitNotBlank
|
|
import io.legado.app.utils.splitNotBlank
|
|
|
-import kotlinx.coroutines.delay
|
|
|
|
|
import kotlinx.coroutines.runBlocking
|
|
import kotlinx.coroutines.runBlocking
|
|
|
import okhttp3.MediaType.Companion.toMediaType
|
|
import okhttp3.MediaType.Companion.toMediaType
|
|
|
import okhttp3.OkHttpClient
|
|
import okhttp3.OkHttpClient
|
|
@@ -86,7 +85,6 @@ class AnalyzeUrl(
|
|
|
companion object {
|
|
companion object {
|
|
|
val paramPattern: Pattern = Pattern.compile("\\s*,\\s*(?=\\{)")
|
|
val paramPattern: Pattern = Pattern.compile("\\s*,\\s*(?=\\{)")
|
|
|
private val pagePattern = Pattern.compile("<(.*?)>")
|
|
private val pagePattern = Pattern.compile("<(.*?)>")
|
|
|
- private val concurrentRecordMap = hashMapOf<String, ConcurrentRecord>()
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var ruleUrl = ""
|
|
var ruleUrl = ""
|
|
@@ -110,6 +108,7 @@ class AnalyzeUrl(
|
|
|
private val enabledCookieJar = source?.enabledCookieJar ?: false
|
|
private val enabledCookieJar = source?.enabledCookieJar ?: false
|
|
|
private val domain: String
|
|
private val domain: String
|
|
|
private var webViewDelayTime: Long = 0
|
|
private var webViewDelayTime: Long = 0
|
|
|
|
|
+ private val concurrentRateLimiter = ConcurrentRateLimiter(source)
|
|
|
|
|
|
|
|
// 服务器ID
|
|
// 服务器ID
|
|
|
var serverID: Long? = null
|
|
var serverID: Long? = null
|
|
@@ -331,99 +330,6 @@ class AnalyzeUrl(
|
|
|
?: ""
|
|
?: ""
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 开始访问,并发判断
|
|
|
|
|
- */
|
|
|
|
|
- @Throws(ConcurrentException::class)
|
|
|
|
|
- private fun fetchStart(): ConcurrentRecord? {
|
|
|
|
|
- source ?: return null
|
|
|
|
|
- val concurrentRate = source.concurrentRate
|
|
|
|
|
- if (concurrentRate.isNullOrEmpty() || concurrentRate == "0") {
|
|
|
|
|
- return null
|
|
|
|
|
- }
|
|
|
|
|
- val rateIndex = concurrentRate.indexOf("/")
|
|
|
|
|
- var fetchRecord = concurrentRecordMap[source.getKey()]
|
|
|
|
|
- if (fetchRecord == null) {
|
|
|
|
|
- synchronized(concurrentRecordMap) {
|
|
|
|
|
- fetchRecord = concurrentRecordMap[source.getKey()]
|
|
|
|
|
- if (fetchRecord == null) {
|
|
|
|
|
- fetchRecord = ConcurrentRecord(rateIndex > 0, System.currentTimeMillis(), 1)
|
|
|
|
|
- concurrentRecordMap[source.getKey()] = fetchRecord
|
|
|
|
|
- return fetchRecord
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- val waitTime: Int = synchronized(fetchRecord!!) {
|
|
|
|
|
- try {
|
|
|
|
|
- if (!fetchRecord.isConcurrent) {
|
|
|
|
|
- //并发控制非 次数/毫秒
|
|
|
|
|
- if (fetchRecord.frequency > 0) {
|
|
|
|
|
- //已经有访问线程,直接等待
|
|
|
|
|
- return@synchronized concurrentRate.toInt()
|
|
|
|
|
- }
|
|
|
|
|
- //没有线程访问,判断还剩多少时间可以访问
|
|
|
|
|
- val nextTime = fetchRecord.time + concurrentRate.toInt()
|
|
|
|
|
- if (System.currentTimeMillis() >= nextTime) {
|
|
|
|
|
- fetchRecord.time = System.currentTimeMillis()
|
|
|
|
|
- fetchRecord.frequency = 1
|
|
|
|
|
- return@synchronized 0
|
|
|
|
|
- }
|
|
|
|
|
- return@synchronized (nextTime - System.currentTimeMillis()).toInt()
|
|
|
|
|
- } else {
|
|
|
|
|
- //并发控制为 次数/毫秒
|
|
|
|
|
- val sj = concurrentRate.substring(rateIndex + 1)
|
|
|
|
|
- val nextTime = fetchRecord.time + sj.toInt()
|
|
|
|
|
- if (System.currentTimeMillis() >= nextTime) {
|
|
|
|
|
- //已经过了限制时间,重置开始时间
|
|
|
|
|
- fetchRecord.time = System.currentTimeMillis()
|
|
|
|
|
- fetchRecord.frequency = 1
|
|
|
|
|
- return@synchronized 0
|
|
|
|
|
- }
|
|
|
|
|
- val cs = concurrentRate.substring(0, rateIndex)
|
|
|
|
|
- if (fetchRecord.frequency > cs.toInt()) {
|
|
|
|
|
- return@synchronized (nextTime - System.currentTimeMillis()).toInt()
|
|
|
|
|
- } else {
|
|
|
|
|
- fetchRecord.frequency += 1
|
|
|
|
|
- return@synchronized 0
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } catch (e: Exception) {
|
|
|
|
|
- return@synchronized 0
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (waitTime > 0) {
|
|
|
|
|
- throw ConcurrentException(
|
|
|
|
|
- "根据并发率还需等待${waitTime}毫秒才可以访问",
|
|
|
|
|
- waitTime = waitTime
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- return fetchRecord
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 访问结束
|
|
|
|
|
- */
|
|
|
|
|
- private fun fetchEnd(concurrentRecord: ConcurrentRecord?) {
|
|
|
|
|
- if (concurrentRecord != null && !concurrentRecord.isConcurrent) {
|
|
|
|
|
- synchronized(concurrentRecord) {
|
|
|
|
|
- concurrentRecord.frequency -= 1
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 获取并发记录,若处于并发限制状态下则会等待
|
|
|
|
|
- */
|
|
|
|
|
- private suspend fun getConcurrentRecord(): ConcurrentRecord? {
|
|
|
|
|
- while (true) {
|
|
|
|
|
- try {
|
|
|
|
|
- return fetchStart()
|
|
|
|
|
- } catch (e: ConcurrentException) {
|
|
|
|
|
- delay(e.waitTime.toLong())
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* 访问网站,返回StrResponse
|
|
* 访问网站,返回StrResponse
|
|
|
*/
|
|
*/
|
|
@@ -435,8 +341,7 @@ class AnalyzeUrl(
|
|
|
if (type != null) {
|
|
if (type != null) {
|
|
|
return StrResponse(url, HexUtil.encodeHexStr(getByteArrayAwait()))
|
|
return StrResponse(url, HexUtil.encodeHexStr(getByteArrayAwait()))
|
|
|
}
|
|
}
|
|
|
- val concurrentRecord = getConcurrentRecord()
|
|
|
|
|
- try {
|
|
|
|
|
|
|
+ concurrentRateLimiter.withLimit {
|
|
|
setCookie()
|
|
setCookie()
|
|
|
val strResponse: StrResponse
|
|
val strResponse: StrResponse
|
|
|
if (this.useWebView && useWebView) {
|
|
if (this.useWebView && useWebView) {
|
|
@@ -500,9 +405,6 @@ class AnalyzeUrl(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
return strResponse
|
|
return strResponse
|
|
|
- } finally {
|
|
|
|
|
- //saveCookie()
|
|
|
|
|
- fetchEnd(concurrentRecord)
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -521,8 +423,7 @@ class AnalyzeUrl(
|
|
|
* 访问网站,返回Response
|
|
* 访问网站,返回Response
|
|
|
*/
|
|
*/
|
|
|
suspend fun getResponseAwait(): Response {
|
|
suspend fun getResponseAwait(): Response {
|
|
|
- val concurrentRecord = getConcurrentRecord()
|
|
|
|
|
- try {
|
|
|
|
|
|
|
+ concurrentRateLimiter.withLimit {
|
|
|
setCookie()
|
|
setCookie()
|
|
|
val response = getClient().newCallResponse(retry) {
|
|
val response = getClient().newCallResponse(retry) {
|
|
|
addHeaders(headerMap)
|
|
addHeaders(headerMap)
|
|
@@ -545,9 +446,6 @@ class AnalyzeUrl(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
return response
|
|
return response
|
|
|
- } finally {
|
|
|
|
|
- //saveCookie()
|
|
|
|
|
- fetchEnd(concurrentRecord)
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|